Can `deps:[]` also be used with `useClass`?
Yes, deps
may be used by useFactory
or useClass
.
You can see that this is true by looking at the Angular source (5+) for packages\compiler\src\metadata_resolver.ts. If deps
is populated for either useClass
or useFactory
, then deps
will be returned in the provider metadata:
getProviderMetadata(provider: cpl.ProviderMeta): cpl.CompileProviderMetadata {
let compileDeps: cpl.CompileDiDependencyMetadata[] = undefined !;
let compileTypeMetadata: cpl.CompileTypeMetadata = null !;
let compileFactoryMetadata: cpl.CompileFactoryMetadata = null !;
let token: cpl.CompileTokenMetadata = this._getTokenMetadata(provider.token);
if (provider.useClass) {
compileTypeMetadata = this._getInjectableMetadata(provider.useClass, provider.dependencies);
compileDeps = compileTypeMetadata.diDeps; <-- ***HERE***
if (provider.token === provider.useClass) {
// use the compileTypeMetadata as it contains information about lifecycleHooks...
token = {identifier: compileTypeMetadata};
}
} else if (provider.useFactory) {
compileFactoryMetadata = this._getFactoryMetadata(provider.useFactory, provider.dependencies);
compileDeps = compileFactoryMetadata.diDeps; <-- ***HERE***
}
return {
token: token,
useClass: compileTypeMetadata,
useValue: provider.useValue,
useFactory: compileFactoryMetadata,
useExisting: provider.useExisting ? this._getTokenMetadata(provider.useExisting) : undefined,
deps: compileDeps, <-- ***HERE ***
multi: provider.multi
};
}
You can see from packages\compiler\src\view_compiler\provider_compiler.ts that deps
is used to instantiate a multi provider:
if (provider.useClass) {
const depExprs = convertDeps(providerIndex, provider.deps || provider.useClass.diDeps);
expr = ctx.importExpr(provider.useClass.reference).instantiate(depExprs); <-- ***HERE***
} else if (provider.useFactory) {
const depExprs = convertDeps(providerIndex, provider.deps || provider.useFactory.diDeps);
expr = ctx.importExpr(provider.useFactory.reference).callFn(depExprs);<-- ***HERE***
}
The same is true for single providers (https://github.com/angular/angular/blob/5.2.x/packages/compiler/src/view_compiler/provider_compiler.ts#L89).
if (providerMeta.useClass) {
providerExpr = ctx.importExpr(providerMeta.useClass.reference);
flags |= NodeFlags.TypeClassProvider;
deps = providerMeta.deps || providerMeta.useClass.diDeps; <-- ***HERE***
} else if (providerMeta.useFactory) {
providerExpr = ctx.importExpr(providerMeta.useFactory.reference);
flags |= NodeFlags.TypeFactoryProvider;
deps = providerMeta.deps || providerMeta.useFactory.diDeps; <-- ***HERE***
}
So even though it is not well documented, deps
can be used by useClass
or useFactory
.
As an aside, deps
is ignored when using useExisting
or useValue
providers (https://github.com/angular/angular/blob/5.2.x/packages/compiler/src/view_compiler/provider_compiler.ts#L108):
} else if (providerMeta.useExisting) {
providerExpr = o.NULL_EXPR;
flags |= NodeFlags.TypeUseExistingProvider;
deps = [{token: providerMeta.useExisting}];
} else {
providerExpr = convertValueToOutputAst(ctx, providerMeta.useValue);
flags |= NodeFlags.TypeValueProvider;
deps = [];
}
That being said, in the typical case, having a useClass
provider and explicitly naming dependencies in the deps
array is usually not necessary. You should let DI handle that for you implicitly.
I found some obscure use cases for it when trying to implement a static forRoot
method as referenced by these questions here and here.
There are two kinds of providers:
StaticProvider and Provider
StaticProvider
It's kind of providers that are used to configure Injector in a static way(without Reflection).
According to the commit
platformXXXX() no longer accepts providers which depend on reflection. Specifically the method signature went from Provider[] to StaticProvider[].
Changelog
What does it mean?
When we pass provider to platform we have to specify
deps
because we have to useStaticClassProvider
orConstructorProvider
instead of justClassProvider
(see picture above).platformBrowserDynamic().bootstrapModule(AppModule, { providers: [ { provide: ElementSchemaRegistry, useClass: CustomDomElementSchemaRegistry, deps: [] <===================== required here } ] } );
When we create Injector dynamically we have to specify
deps
becauseInjector.create
takesStaticProvider
array.
For instance:
export const MyParams = new InjectionToken<string[]>('params');
export class MyService {
constructor(@Inject(MyParams) public someParameters: string[]) {}
}
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
name = 'Angular ' + VERSION.full;
constructor() {
const inj = Injector.create([
{ provide: MyService, useClass: MyService } <=== we will get an error because we have to define deps
])
}
}
https://ng-run.com/edit/5Xm4jwAoXXyAIspwF571
Provider
It's kind of providers that we usually use when write providers in @NgModule or @Component/@Directive metadata
Looking at this answer: how the parameters of a forRoot() module's method is passed to a provider? I would say that deps
is not required there. We only need to provide Params
in providers array and angular will do all job for us.
@estus said that:
deps are available only in useFactory providers but not in useClass providers.
because he meant Provider
(more precisely ClassProvider
) not StaticProvider
.
P.S. You can also read my article about StaticInjector
:)
- https://medium.com/@a.yurich.zuev/angular-how-staticinjector-replaces-reflectiveinjector-6f303d2798f6
deps
are available only in useFactory
providers but not in useClass
providers.
That's because the method that was chosen by Angular team for DI annotation as preferable (emitted type metadata) is applicable only to class constructors. While regular functions that are used in useFactory
providers can't make use of type metadata and need alternative annotation method, which is dep
array.
As explained in this answer, classes can be alternatively annotated for DI with parameters
static property. Both class provider parameters
and factory provider deps
accept an array consisting of provider tokens or arrays of decorator instances (like [new Optional(), new Inject(...)]
).