1

I have authored a library for extracting environment variables that are set from server-side rendering.

This library provides a base class to extend. getEnvironmentValues() has been omitted for brevity:

@Directive()
export class NgxEnvironmentService<T> {

  environment: T;

  constructor(
    @Inject(ENVIRONMENT_CONFIG)
    private readonly environmentConfig: IEnvironmentConfig,

    @Inject(PLATFORM_ID)
    private readonly platformId: string,
  ) {
    if (isPlatformBrowser(this.platformId)) {
      this.environment = this.getEnvironmentValues<T>();
    }
  }

}

Here is the ENVIRONMENT_CONFIG token:

import { InjectionToken } from '@angular/core';

import { DEFAULT_CONFIG } from './constants';
import { IEnvironmentConfig } from '../interfaces';

export const ENVIRONMENT_CONFIG = new InjectionToken<IEnvironmentConfig>('environment-config', {
  factory: (): IEnvironmentConfig => DEFAULT_CONFIG,
  providedIn: 'root'
});

This library is transpiled using ng-packagr. After installation and implementation in the target project:

import { Injectable } from '@angular/core';
import { NgxEnvironmentService } from '@labcorp/ngx-environment';

import { IEnvironment } from '../interfaces';

@Injectable({
  providedIn: 'root'
})
export class EnvironmentService extends NgxEnvironmentService<IEnvironment> {}

I receive the following error:

The injectable EnvironmentService inherits its constructor from NgxEnvironmentService, but the latter has a constructor parameter that is not compatible with dependency injection. Either add an explicit constructor to EnvironmentService or change NgxEnvironmentService's constructor to use parameters that are valid for DI.

If I take the source code from the library and copy it into the target project and change the import path, everything works as expected.

This issue: https://stackoverflow.com/questions/60702258/angular-ivy-constructor-is-not-compatible-with-angular-dependency-injection#:~:text=core.js%3A3828%20ERROR%20Error%3A%20This%20constructor%20is%20not%20compatible,of%20this%20class%20is%20missing%20an%20Angular%20decorator is similar, but I'd really like to avoid having to re-implement the constructor in classes that extend NgxEnvironmentService

Has anyone else experienced this error, and how did you fix it?

Brandon Taylor
  • 33,823
  • 15
  • 104
  • 144
  • I think you need to add the `constructor` to your `EnvironmentService`, and call the `super(...)` constructor with the appropriate dependencies/parameters. – Pieterjan Nov 24 '22 at 07:02
  • The services required by the `super(...)` constructor, you need to inject in your `EnvironmentService` and pass on to the `super(...)` constructor – Pieterjan Nov 24 '22 at 07:03
  • I don't think this is the case. When I use the library code directly in my project, everything works as expected. It's only after the library is built with ng-packagr and installed as a dependency that the problem occurs. – Brandon Taylor Nov 24 '22 at 13:21

1 Answers1

2

There were two underlying problems causing this behavior.

First was this issue: https://github.com/angular/angular/issues/45155 which was resolved with the release of Angular 15.0.1: https://github.com/angular/angular/issues/46419

Setting strictInjectionParameters to false in my target project fixed the compiler issue.

Second was that during testing, I was specifying a local install path for the library in my target project. Even though I had not included @angular/core as a dependency in the library's package.json, the target project was apparently confused as to which @angular/core to use, resulting in this error at runtime:

ERROR Error: NG0203: inject() must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with `EnvironmentInjector#runInContext`. Find more at https://angular.io/errors/NG0203
    at injectInjectorOnly (core.mjs:731:15)
    at Module.ɵɵinject (core.mjs:742:60)
    at NgxEnvironmentService_Factory (ngx-environment.service.ts:11:35)
    at Object.EnvironmentService_Factory [as factory] (environment.service.ts:9:32)
    at R3Injector.hydrate (core.mjs:8780:35)
    at R3Injector.get (core.mjs:8668:33)
    at ChainedInjector.get (core.mjs:13929:36)
    at lookupTokenUsingModuleInjector (core.mjs:3547:39)
    at getOrCreateInjectable (core.mjs:3592:12)
    at Module.ɵɵdirectiveInject (core.mjs:10971:12)

I set a paths value in my tsconfig.json:

"paths": {
  "@angular/*": ["node_modules/@angular/*"],
}

which fixed the runtime error, however, after publishing my library to npm and installing from there, I was able to remove the path and the project now runs as expected.

Brandon Taylor
  • 33,823
  • 15
  • 104
  • 144