import type { Token } from "./tokens.ts"; import { type Class } from "./utils.ts"; import type { Container } from "./container.ts"; /** * A provider states how, for a given token, a service should be constructed. */ export type Provider = SyncProvider | AsyncProvider; /** * A provider that provides synchronously, allowing a non-blocking process. */ export type SyncProvider = ConstructorProvider | ClassProvider | ValueProvider | SyncFactoryProvider | ExistingProvider; /** * A provider that provides asynchronously, enforcing an awaitable process. */ export type AsyncProvider = AsyncFactoryProvider; /** * A factory provider refers to a value which is lazily returned. */ export type FactoryProvider = SyncFactoryProvider | AsyncFactoryProvider; /** * A constructor provider refers to a class constructor, * which is the same class as the token itself. */ export type ConstructorProvider = Class; /** * A class provider refers to a class constructor, * which may be the same class as the token, or a subclass. */ export interface ClassProvider { provide: Token; useClass: Class>; multi?: true; } /** * Provides a static value. */ export interface ValueProvider { provide: Token; useValue: T; multi?: true; } /** * Provides a value which is lazily returned by a synchronous factory function. */ export interface SyncFactoryProvider { provide: Token; async?: false; multi?: true; useFactory: (container: Container) => NoInfer; } /** * Provides a value which is lazily returned by an asynchronous factory function. */ export interface AsyncFactoryProvider { provide: Token; async: true; multi?: true; useFactory: (container: Container) => Promise>; } /** * Provides a value that is provided by another provider. */ export interface ExistingProvider { provide: Token; useExisting: Token; multi?: boolean; } export declare function isConstructorProvider(provider: Provider): provider is ConstructorProvider; export declare function isClassProvider(provider: Provider): provider is ClassProvider; export declare function isValueProvider(provider: Provider): provider is ValueProvider; export declare function isFactoryProvider(provider: Provider): provider is FactoryProvider; export declare function isAsyncProvider(provider: Provider): provider is AsyncProvider; export declare function isExistingProvider(provider: Provider): provider is ExistingProvider; export declare function isMultiProvider(provider: Provider): boolean; /** * A single provider, or an arbitrarily nested array of providers. */ type ProviderNode = Provider | readonly ProviderNode[]; /** * Recursively unwraps nested arrays, yielding the union of all leaf types. */ type ExtractProviders = T extends readonly unknown[] ? ExtractProviders : T; /** * Every provider variant that is an object literal, i.e. all of them except * {@link ConstructorProvider}, which is a class reference. */ type ObjectProvider = ClassProvider | ValueProvider | SyncFactoryProvider | AsyncFactoryProvider | ExistingProvider; /** * `keyof` distributed over the members of a union, rather than the keys they have in common. */ type KeysOfUnion = T extends unknown ? keyof T : never; /** * Every property name that may appear on an object-based provider. */ type ProviderKey = KeysOfUnion>; /** * Rejects properties that do not exist on any provider, by requiring them to be `never`. * * TypeScript only applies its built-in excess property check to *fresh* object literals. * Since {@link ProviderList} first captures the arguments in an inferred type parameter, * that freshness is lost, so the check is reproduced here explicitly. */ type NoExcessProperties = Record, never>; /** * How many levels of nesting {@link CheckedProviderList} descends into, expressed as the * remaining budget. * * The recursion between {@link CheckProviderNode} and {@link CheckedProviderList} is otherwise * unbounded. For a concrete argument list that is harmless, since the nesting runs out. But for a * type parameter that is not resolved yet, TypeScript cannot tell whether an element is a nested * array, so it keeps expanding both branches. That happens as soon as `Container` takes part in a * structural comparison, for example when a mapped type over it is checked against another, and it * makes the checker give up with "Type instantiation is excessively deep and possibly infinite". * * Four levels covers any realistic provider list. Deeper elements degrade to `Provider`, * which still accepts every valid provider, but no longer correlates a token with its value type. */ type NestingBudget = [unknown, unknown, unknown, unknown]; /** * Type-checks a single element: either a nested array, or a provider whose value type * is correlated with the token it is provided for. */ type CheckProviderNode = Budget extends readonly [unknown, ...infer Rest] ? T extends readonly unknown[] ? CheckedProviderList : T extends { provide: Token; } ? Exclude extends never ? Provider : Provider & NoExcessProperties : Provider : Provider; /** * Type-checks every element of an (arbitrarily nested) list of providers. */ type CheckedProviderList = { [K in keyof T]: CheckProviderNode; }; /** * The list of arguments accepted by {@link Container.bindAll} and {@link defineProviders}: * one or more providers, optionally nested in arrays, preserving the correlation between each * token and the value it provides. * * When the given arguments already satisfy this, they are accepted as-is. Otherwise this type * resolves to the expected shape, so that TypeScript reports the mismatch on the offending * element rather than on the call as a whole. */ export type ProviderList = T extends readonly [] ? readonly [ProviderNode] : T extends CheckedProviderList ? T : CheckedProviderList; /** * Defines a list of providers upfront, outside of a container. * * Providers may be passed individually or as (nested) arrays, and are returned as a single * flattened array. Unlike annotating a variable as `Provider[]`, this preserves the * correlation between each token and the value it provides. * * @param providers one or more providers, optionally nested in arrays * @returns a flat array containing every given provider * * {@link https://needle-di.io/concepts/binding.html#defining-providers-upfront} */ export declare function defineProviders(...providers: ProviderList): ExtractProviders[]; export {};