API @deepkit/injector
npm install @deepkit/injector
Class describing where a transient provider will be injected. This is the actual dependency injection container.
Every module has its own injector. A InjectorContext is responsible for taking a root InjectorModule and build all Injectors. It also can create scopes aka a sub InjectorContext with providers from a particular scope. Returns a value that can be compared with This is used in the big switch-case statement in the generated code to match DI tokens. Returns the injector token type if the given type was decorated with Checks if given type is nominally compatible with the given interface. 0 means not compatible, 1 means exactly compatible, n>1 means compatible but not exactly. The higher the number the further away the compatibility is (the inheritance chain). Wraps the given component with a new injector context. A factory function for some class.
All properties that are not provided will be resolved using the injector that was used to create the factory.Classes
export class TransientInjectionTarget {
constructor(public readonly token: Token);
}
export class Injector {
constructor(public readonly module: InjectorModule, private buildContext: BuildContext);
static from(providers: ProviderWithScope[], parent?: Injector): Injector;
static fromModule(module: InjectorModule): Injector;
get<T>(token?: ReceiveType<T> | Token<T>, scope?: Scope, optional: boolean = false): ResolveToken<T>;
set<T>(token: Token<T>, value: any, scope?: Scope): void;
instantiationCount(token: any, scope?: Scope): number;
getSetter<T>(token: ReceiveType<T> | Token<T>): Setter<T>;
getResolver<T>(token?: ReceiveType<T> | Token<T>, label?: string): Resolver<T>;
clear();
createSetter<T>(token: ReceiveType<T> | Token<T>, scope?: Scope, label?: string): Setter<T>;
createResolver<T>(token: ReceiveType<T> | Token<T>, scope?: Scope, label?: string): Resolver<T>;
}
export class BuildContext {
static ids: number;
id: number;
runtimeContext;
tagRegistry: TagRegistry;
providerIndex: BuildProviderIndex;
/**
* In the process of preparing providers, each module redirects their
* global setup calls in this registry.
*/
globalConfigurationProviderRegistry: ConfigurationProviderRegistry;
}
export class InjectorContext {
constructor(public rootModule: InjectorModule, public scope?: Scope);
/**
* Returns a resolver for the given token. The returned resolver can
* be executed to resolve the token. This increases performance in hot paths.
*/
resolve<T>(module?: InjectorModule, type?: ReceiveType<T> | Token<T>): Resolver<T>;
setter<T>(module?: InjectorModule, type?: ReceiveType<T> | Token<T>): Setter<T>;
/**
* Returns an instance of the given token or type from the injector associated with the specified module.
*
* If there is no provider for the token or the provider returns undefined, it returns undefined.
*/
getOrUndefined<T>(token?: ReceiveType<T> | Token<T>, module?: InjectorModule): ResolveToken<T> | undefined;
/**
* Returns an instance of the given token or type from the injector associated with the specified module.
*
* If there is no provider for the token or the provider returns undefined, it throws an error.
*/
get<T>(token?: ReceiveType<T> | Token<T>, module?: InjectorModule): ResolveToken<T>;
/**
* Returns the instantiation count of the given token.
*
* This is either 0 or 1 for normal providers, and >= 0 for transient or scoped providers.
*/
instantiationCount(token: Token, module?: InjectorModule, scope?: string): number;
/**
* Sets a value for the given token in the injector associated with the specified module.
*
* This is useful for scoped providers like HttpRequest that are created dynamically
* outside the injector container and need to be injected into services.
*/
set<T>(token: T, value: any, module?: InjectorModule): void;
static forProviders(providers: ProviderWithScope[]);
getInjector(module: InjectorModule): Injector;
getRootInjector(): Injector;
createChildScope(scope: string): InjectorContext;
}
export class TagRegistry {
constructor(public tags: TagRegistryEntry<any>[] = []);
register(tagProvider: TagProvider<any>, module: InjectorModule);
resolve<T extends ClassType<Tag<any>>>(tag: T): TagRegistryEntry<InstanceType<T>>[];
}
export class TagProvider<T> {
constructor(public provider: NormalizedProvider<T>, public tag: Tag<T>);
}
export class Tag<T, TP extends TagProvider<T> = TagProvider<T>> {
_: () => T;
_2: () => TP;
constructor(public readonly services: T[] = []);
static provide<P extends ClassType<T> | ValueProvider<T> | ClassProvider<T> | ExistingProvider<T> | FactoryProvider<T>, T extends ReturnType<InstanceType<B>[]>, TP extends ReturnType<InstanceType<B>[]>, B extends ClassType<Tag<any>>>(this: B, provider: P): TP;
}
export class ConfigurationProviderRegistry {
configurations: ConfigureProviderEntry[];
add(type: Type, call: Function, options: ConfigureProviderOptions);
mergeInto(registry: ConfigurationProviderRegistry): void;
get(token: Token): ConfigureProviderEntry[];
}
export class InjectorModule<C extends InjectorModuleConfig = any> {
id: number;
/**
* Whether this module is for the root module. All its providers are automatically exported and moved to the root level.
*/
root: boolean;
/**
* The built injector. This is set once an Injector for this module has been created.
*/
injector?: Injector;
configurationProviderRegistry: ConfigurationProviderRegistry;
globalConfigurationProviderRegistry: ConfigurationProviderRegistry;
imports: InjectorModule[];
configDefinition?: ClassType;
constructor(public providers: ProviderWithScope[] = [], public parent?: InjectorModule, public config: RemovedUndefined<C> = {} as RemovedUndefined<C>, public exports: ExportType[] = []);
registerAsChildren(child: InjectorModule): void;
/**
* When the module exports providers the importer doesn't want, then `disableExports` disable all exports.
*/
disableExports(): this;
/**
* Makes all the providers, controllers, etc available at the root module, basically exporting everything.
*/
forRoot(): this;
/**
* Reverts the root default setting to false.
*/
notForRoot(): this;
unregisterAsChildren(child: InjectorModule): void;
getChildren(): InjectorModule[];
setConfigDefinition(config: ClassType): this;
setParent(parent: InjectorModule): this;
getParent(): InjectorModule | undefined;
addExport(...types: (ExportType | ExportType[])[]): this;
isExported(token: Token): boolean;
isProvided<T>(token?: Token<T>, type?: ReceiveType<T>): boolean;
addProvider(...provider: (ProviderWithScope | ProviderWithScope[])[]): this;
getProviders(): ProviderWithScope[];
getConfig(): C;
configure(config: Partial<C>): this;
getImports(): InjectorModule[];
getImportedModulesByClass<T extends InjectorModule>(classType: ClassType<T>): T[];
getImportedModuleByClass<T extends InjectorModule>(classType: ClassType<T>): T;
getImportedModule<T extends InjectorModule>(module: T): T;
getExports();
hasImport<T extends InjectorModule>(moduleClass: ClassType<T>): boolean;
/**
* Adds a new import at the end.
*/
addImport(...modules: InjectorModule<any>[]): this;
/**
* Adds a new import at the beginning. Since import order matters, it might be useful to import a module first
* so its exported providers can be overwritten by imports following this module.
*/
addImportAtBeginning(...modules: InjectorModule<any>[]): this;
/**
* Configures a provider by applying a custom configuration function to its instance.
* The passed configure function is executed when instance was created.
* If the provider is in a scope and the scope created multiple instances,
* the configure function is executed for each instance.
*
* The purpose of a provider configuration is to configure the instance, for example
* call methods on it, set properties, etc.
*
* The first parameter of the function is always the instance of the provider that was created.
* All additional defined parameters will be provided by the dependency injection container.
*
* if `options.replace` is true, the returned value of `configure` will
* replace the instance.
* if `options.global` is true, the configuration function is applied to all
* providers in the whole module tree.
* The `options.order` defines the order of execution of the configuration function.
* The lower the number, the earlier it is executed.
*/
configureProvider<T>(configure: (instance: T, ...args: any[]) => any, options: Partial<ConfigureProviderOptions> = {}, type?: ReceiveType<T>): this;
getOrCreateInjector(buildContext?: BuildContext): Injector;
getPreparedProvider(token: Token, candidate?: PreparedProvider): PreparedProvider | undefined;
getSetupProvider(token: Token, candidate?: PreparedProvider): PreparedProvider | undefined;
resolveToken(token: Token): InjectorModule | undefined;
getBuiltPreparedProviders(): PreparedProvider[] | undefined;
/**
* Prepared the module for an injector tree build.
*
* - Index providers by token so that last known provider is picked (so they can be overwritten).
* - Register TagProvider in TagRegistry
* - Put TagProvider in providers if not already made.
* - Put exports to parent's module with the reference to this, so the dependencies are fetched from the correct module.
*/
getPreparedProviders(buildContext: BuildContext): PreparedProvider[];
findRoot(): InjectorModule;
}
Errors
export class InjectorError extends CustomError {
}
export class CircularDependencyError extends InjectorError {
}
export class ServiceNotFoundError extends InjectorError {
}
export class DependenciesUnmetError extends InjectorError {
}
Functions
(token: Token): string
(provider: ProviderWithScope): Token
(type: Type): ContainerToken
(obj: any): obj is Type
(type: Token): ContainerToken
===
to check if two tokens are actually equal even though
they are the result of different type expressions.(type: Type): Type | undefined
Inject<T>
.<T extends (...args: any) => any>(fn: T, injector: Injector, skipParameters?: number, type?: Type, skipTypeParameters?: number): ((scope?: Scope, ...args: any[]) => ReturnType<T>)
(type: Type | undefined, injector: Injector): (scope?: Scope) => <T>(partial: Partial<{ [K in keyof T]: T[K]; }>) => T
<T>(provider?: (ProviderBase & ProviderScope & ({ useValue?: T; } | { useClass: ClassType; } | { useExisting: any; } | { useFactory: (...args: any[]) => T | undefined; })) | ClassType | ((...args: any[]) => T), type?: ReceiveType<T>): NormalizedProvider
(obj: any): obj is ProviderProvide & ProviderScope
(obj: any): obj is ValueProvider<any>
(obj: any): obj is ClassProvider<any>
(obj: any): obj is ExistingProvider<any>
(obj: any): obj is FactoryProvider<any>
(obj: any): obj is Provider<any>
(provider: ProviderWithScope): boolean
(providers: ProviderWithScope[], requestScope: "module" | "session" | "request" | string): Provider<any>[]
(config: ClassType, modules: InjectorModule[]): { module: InjectorModule; path: string; } | undefined
<T>(providers: ProviderWithScope[], token: Token<T>): boolean
(provider: ProviderWithScope): string
(token: Type, provider: Type): number
<T extends Function>(fn: T & { __injected?: any; }, container: InjectorContext): any
<T extends Function>(fn: T, providers: ProviderWithScope[]): T
(props: { providers?: ProviderWithScope[]; module?: InjectorModule; state?: ClassType; children?: any; }): any
Types
interface Scope {
name: string;
}
type ResolveToken<T> = T extends ClassType<infer R> ? R : T extends AbstractClassType<infer R> ? R : T;
type ContainerToken = symbol | number | bigint | boolean | string | AbstractClassType<unknown> | Function;
type PartialFactory<C> = (args: Partial<{ [K in keyof C]: C[K] }>) => C;
interface Resolver<T> {
(scope: Scope | undefined, optional: true): T | undefined;
(scope?: Scope, optional?: boolean): T;
}
type Setter<T> = (value: T, scope?: Scope, optional?: boolean) => void;
interface ProviderBase {
/**
* Per default all instances are singleton (scoped to its scope). Enabling transient makes the
* Injector create always a new instance for every consumer.
*/
transient?: true;
}
interface ProviderScope {
scope?: 'rpc' | 'http' | 'cli' | string;
}
type Token<T = unknown> = symbol | number | bigint | boolean | string | AbstractClassType<T> | Type | TagProvider<T> | Function | T;
interface ValueProvider<T> extends ProviderBase {
/**
* An injection token.
*/
provide: Token<T>;
/**
* The value to inject.
*/
useValue: T;
}
interface ClassProvider<T> extends ProviderBase {
/**
* An injection token.
*/
provide: Token<T>;
/**
* Class to instantiate for the `token`.
*/
useClass?: ClassType<T>;
}
interface ExistingProvider<T> extends ProviderBase {
/**
* An injection token.
*/
provide: Token<T>;
/**
* Existing `token` to return. (equivalent to `injector.get(useExisting)`)
*/
useExisting: ClassType<T>;
}
interface FactoryProvider<T> extends ProviderBase {
/**
* An injection token.
*/
provide: Token<T>;
/**
* A function to invoke to create a value for this `token`.
*/
useFactory: (...args: any[]) => T | undefined;
}
type Provider<T = any> = AbstractClassType | ValueProvider<T> | ClassProvider<T> | ExistingProvider<T> | FactoryProvider<T> | TagProvider<T>;
type ProviderProvide<T = any> = ValueProvider<T> | ClassProvider<T> | ExistingProvider<T> | FactoryProvider<T>;
type NormalizedProvider<T = any> = ProviderProvide<T> & ProviderScope;
type ProviderWithScope<T = any> = AbstractClassType | (ProviderProvide<T> & ProviderScope) | TagProvider<any>;
interface ConfigureProviderOptions {
/**
* If there are several registered configuration functions for the same token,
* they are executed in order of their `order` value ascending. The default is 0.
* The lower the number, the earlier it is executed.
*/
order: number;
/**
* Replaces the instance with the value returned by the configuration function.
*/
replace: boolean;
/**
* Per default only providers in the same module are configured.
* If you want to configure providers of all modules, set this to true.
*/
global: boolean;
}
interface ConfigureProviderEntry {
type: Type;
options: ConfigureProviderOptions;
call: Function;
}
interface PreparedProvider {
token: Token;
/**
* The modules from which dependencies can be resolved. The first item is always the module from which this provider was declared.
*
* This is per default the module in which the provider was declared,
* but if the provider was moved (by exporting), then if
* a) the parent had this provider already, then this array has additionally the one from which the provider was exported.
* b) the parent had no provider of that token, then this array is just the module from which the provider was exported.
*
* This is important otherwise exported provider won't have access in their dependencies to their original (encapsulated) injector.
*/
modules: InjectorModule[];
/**
* A token can have multiple providers, for each scope its own entry.
* Each scoped provider can only exist once.
*/
providers: NormalizedProvider[];
/**
* When this provider was exported to another module and thus is actually instantiated in another module, then this is set.
* This is necessary to tell the module who declared this provider to not instantiate it, but redirects resolve requests
* to `resolveFrom` instead.
*/
resolveFrom?: InjectorModule;
}
type ExportType = Token | InjectorModule;
type InjectorModuleConfig = { [name: string]: any } | undefined;