@snap/camera-kit
Version:
Camera Kit Web
86 lines • 5.41 kB
TypeScript
import type { Memoized } from "../common/memoize";
import type { Container } from "./Container";
import type { AddService, InjectableFunction, ServicesFromTokenizedParams, ValidTokens } from "./types";
type AddDependencies<ParentDependencies, Dependencies> = ParentDependencies extends any ? {
[K in keyof ParentDependencies | keyof Dependencies]: K extends keyof ParentDependencies ? ParentDependencies[K] : K extends keyof Dependencies ? Dependencies[K] : never;
} : never;
type ExcludeKey<T, U> = T extends any ? {
[K in Exclude<keyof T, U>]: T[K];
} : never;
type PartialInjectableFunction<Params extends readonly any[], Tokens extends readonly string[], Token extends string, Service> = {
(...args: Params): Service;
token: Token;
dependencies: Tokens;
};
type Injectables<Services, Dependencies> = {
[K in keyof Services]: K extends string ? InjectableFunction<Services & Dependencies, readonly ValidTokens<Services & Dependencies>[], K, Services[K]> : never;
};
type PartialContainerFactories<Services, Dependecies> = {
[K in keyof Services]: Memoized<(c: Container<Partial<Services> & Dependecies>) => Services[K]>;
};
/**
* Similar to [Container], with the exception that Services may be provided to a PartialContainer which *does not*
* contain all of that Services dependencies.
*
* For this to remain safe, Services can not be resolved by PartialContainer – it has no `get` method.
*
* Instead, the PartialContainer must be provided to a [Container] which *does* contain all the dependencies required
* by all the Service in the PartialContainer. The resulting [Container] can then resolve these Services.
*
* PartialContainers are used to create a collection of Services which can then be provided via a simple one-line syntax
* to an existing Container (which fulfills the collection's dependencies). It is an organizational tool, allowing
* coherent groupings of Services to be defined in one place, then combined elsewhere to form a complete [Container].
*
* Here's an example of PartialContainer usage:
* ```ts
* // We can provide fooFactory, even though the PartialContainer doesn't fulfill the Bar dependency.
* const fooFactory = Injectable('Foo', ['Bar'] as const, (bar: Bar) => new Foo(bar))
* const partialContainer = new PartialContainer({}).provide(fooFactory)
*
* const barFactory = Injectable('Bar', () => new Bar())
* const dependenciesContainer = Container.provides(barFactory)
*
* const combinedContainer = dependenciesContainer.provides(partialContainer)
*
* // We can resolve Foo, because the combined container includes Bar, so all of Foo's dependencies are now met.
* const foo = combinedContainer.get('Foo')
* ```
*/
/** @internal */
export declare class PartialContainer<Services = {}, Dependencies = {}> {
private readonly injectables;
constructor(injectables: Injectables<Services, Dependencies>);
/**
* Create a new PartialContainer which provides a Service created by the given InjectableFunction.
*
* The InjectableFunction contains metadata specifying the Token by which the created Service will be known, as well
* as an ordered list of Tokens to be resolved and provided to the InjectableFunction as arguments.
*
* This dependencies are allowed to be missing from the PartialContainer, but these dependencies are maintained as a
* parameter of the returned PartialContainer. This allows `[Container.provides]` to type check the dependencies and
* ensure they can be provided by the Container.
*
* @param fn A InjectableFunction, taking dependencies as arguments, which returns the Service.
*/
provides<AdditionalDependencies extends readonly any[], Tokens extends readonly string[], Token extends string, Service>(fn: PartialInjectableFunction<AdditionalDependencies, Tokens, Token, Service>): PartialContainer<AddService<Services, Token, Service>, ExcludeKey<AddDependencies<ExcludeKey<Dependencies, Token>, ServicesFromTokenizedParams<Tokens, AdditionalDependencies>>, keyof Services>>;
/**
* In order to create a [Container], the InjectableFunctions maintained by the PartialContainer must be memoized
* into Factories that can resolve their dependencies and return the correct Service.
*
* In particular, this requires access to a "parent" Container to avoid infinite looping in cases where Service A
* depends on Service A – this is allowed (as long as the parent container provides Service A), but requires access
* to the parent Container to provide the parent implementation of Service A.
*
* This also means that Services provided by a PartialContainer to a Container via this function will always be
* scoped to the Container. In other words, if a PartialContainer containing Service A is provided to both
* Container X and Container Y, when Service A is resolved by Container X the InjectableFunction used to create
* Service A will be invoked – and when Service A is resolved by Container Y, the InjectableFunction will be invoked
* again.
*
* @param parent A [Container] which provides all the required Dependencies of this PartialContainer.
*/
getFactories(parent: Container<Dependencies>): PartialContainerFactories<Services, Dependencies>;
getTokens(): Array<keyof Services>;
}
export {};
//# sourceMappingURL=PartialContainer.d.ts.map