UNPKG

@rmk-labs/typescript-dependency-injector

Version:
175 lines (170 loc) 6.72 kB
declare const PROVIDER_SYMBOL: unique symbol; interface Provider<T> { provide: (...args: any[]) => T; } declare abstract class BaseProvider<T> implements Provider<T> { private [PROVIDER_SYMBOL]; private _overridingProviders; abstract provide(...args: any[]): T; /** * Returns a readonly copy of the overriding providers stack. */ get overrides(): readonly Provider<T>[]; /** * Overrides the current provider with another provider. * When the provider is called, it will delegate to the last overriding provider. * @param provider - The provider to override with * @throws Error if the argument is not a provider */ override(provider: Provider<T>): void; /** * Resets the last overriding provider and returns it. * @returns The provider that was removed from the override stack * @throws Error if there are no overriding providers */ resetLastOverriding(): Provider<T>; /** * Resets all overriding providers at once and returns them. * @returns An array of all providers that were removed from the override stack */ resetOverride(): Provider<T>[]; /** * Returns true if the provider is currently overridden, false otherwise. */ isOverridden(): boolean; /** * Returns a Delegate provider that wraps this provider. * The Delegate's provide() method returns this provider instance. */ get provider(): Delegate<T>; /** * Protected method for providers to delegate to the overriding provider if one exists. * Should be called at the beginning of each provider's provide() method. */ protected _delegateIfOverridden(...args: any[]): T | undefined; } /** * Type representing a constructable that can create instances of T. * Can be either a class constructor or a factory function. */ type FactoryConstructable<T> = (new (...args: any[]) => T) | ((...args: any[]) => T); declare class Factory<T, ProvideArgs extends any[] = any[]> extends BaseProvider<T> { private factory; private injectedArgs; private isConstructor; constructor(factory: FactoryConstructable<T>, ...injectedArgs: any[]); provide(...args: ProvideArgs): T; /** * Resolves an argument, handling Extend instances specially. * If the argument is an Extend instance and context args are provided, * merges the context with defaults (context takes priority). */ private _resolveArg; } declare class Singleton<T, ProvideArgs extends any[] = any[]> extends Factory<T, ProvideArgs> { private instance; provide(...args: ProvideArgs): T; resetInstance(): void; } /** * Delegate provider that wraps another provider. * When provide() is called, it returns the wrapped provider instance itself. * This is useful for dependency injection scenarios where you want to inject * the provider rather than the provided value. */ declare class Delegate<T> extends BaseProvider<Provider<T>> { private readonly delegatedProvider; constructor(delegatedProvider: Provider<T>); provide(): Provider<T>; } /** * Base class for declarative dependency injection containers. * Extend this class and define provider properties to create a container. * * @example * ```ts * class MyContainer extends DeclarativeContainer { * databaseConfig = new Factory(DatabaseConfig, "localhost", 5432, "myapp"); * database = new Singleton(Database, this.databaseConfig); * } * * const container = new MyContainer(); * * // Access providers via instance properties * const db = container.database.provide(); * const config = container.databaseConfig.provide(); * ``` */ declare abstract class DeclarativeContainer { resetProviderOverrides(): void; resetSingletonInstances(): void; } type Constructor<T = object> = new (...args: any[]) => T; type AnyFunction = Function; type IsProviderLike<T> = T extends BaseProvider<any> ? true : T extends { provide: (...args: any[]) => any; } ? true : false; type ProviderKeys<T> = { [K in keyof T]-?: IsProviderLike<T[K]> extends true ? K : never; }[keyof T]; type InjectableMarkers<TContainer extends DeclarativeContainer> = { [K in ProviderKeys<TContainer>]: ParameterDecorator & { provider: ParameterDecorator; }; }; type InjectAPI<TContainer extends DeclarativeContainer> = InjectableMarkers<TContainer> & { wire: (container: TContainer) => void; unwire: (container: TContainer) => void; Injectable: <T extends Constructor>(target: T) => T; }; declare function createInject<TCtor extends Constructor<DeclarativeContainer>>(options: { containerClass: TCtor; }): InjectAPI<InstanceType<TCtor>>; declare function getInjectedParamIds(fn: AnyFunction): ReadonlyMap<number, symbol> | undefined; declare function getMarkerFor<TCtor extends Constructor<DeclarativeContainer>>(containerCtor: TCtor, key: ProviderKeys<InstanceType<TCtor>> & PropertyKey): symbol; declare function InstanceOf<T>(): T; declare function InstanceOf<T>(_type: T): T extends abstract new (...args: any[]) => any ? InstanceType<T> : T; declare function ProviderOf<T>(): Provider<T>; declare function ProviderOf<T>(_type: T): T extends abstract new (...args: any[]) => any ? Provider<InstanceType<T>> : Provider<T>; /** * Symbol to identify Extend instances */ declare const EXTEND_SYMBOL: unique symbol; /** * Wraps an object to indicate that it should be extended with context values * when the provider's .provide() method is called. * * When a Factory is created with an Extend-wrapped object as an argument, * calling .provide(contextObj) will merge contextObj with the wrapped defaults, * with context values taking priority. * * @example * ```ts * interface ServiceDeps { * logger: Logger; * database: Database; * requestId: string; * } * * class AppContainer extends DeclarativeContainer { * logger = new Singleton(Logger); * database = new Singleton(Database, "localhost"); * * // requestId will come from context at runtime * service = new Factory(Service, Extend({ * logger: this.logger, * database: this.database, * })); * } * * const container = new AppContainer(); * // Context values take priority and extend the defaults * const instance = container.service.provide({ requestId: "req-123" }); * ``` */ declare class Extend<T extends Record<string, any>> { readonly defaults: T; readonly [EXTEND_SYMBOL] = true; constructor(defaults: T); } export { BaseProvider, DeclarativeContainer, Delegate, Extend, Factory, type FactoryConstructable, type InjectAPI, type InjectableMarkers, InstanceOf, type Provider, ProviderOf, Singleton, createInject, getInjectedParamIds, getMarkerFor };