fluxject
Version:
A strongly-typed dependency injection library.
193 lines (192 loc) • 7.71 kB
TypeScript
/**
* Create a new Fluxject Container for managing dependencies.
*
* @example
* import { fluxject } from "fluxject";
* import type { InferServiceProvider } from "fluxject";
* const container = fluxject()
* .register(m => m.singleton({ myService: MyService }))
* .register(m => m.scoped({ myScopedService: MyScopedService }))
* .register(m => m.transient({ myTransientService: MyTransientService }));
*
* const provider = container.prepare();
* provider.myService.doSomething();
* // CLI prints "doing something else!"
* const scope = provider.createScope();
* scope.myScopedService.doScopedSomething();
* // CLI prints "doing something else!"
*
* class MyService {
*
* constructor({ myTransientService }: InferServiceProvider<typeof container, "myService">) {
* this.myTransientService = myTransientService;
* }
*
* doSomething() {
* this.myTransientService.doSomethingElse();
* }
* }
*
* class MyTransientService() {
* doSomethingElse() {
* console.log("doing something else!");
* }
* }
*
* class MyScopedService() {
* constructor({ myService, myTransientService }: InferServiceProvider<typeof container, "myScopedService">) {
* this.myService = myService;
* this.myTransientService = myTransientService;
* }
*
* doScopedSomething() {
* this.myService.doSomething();
* }
* }
*/
export function fluxject(): Container<{}>;
/**
* Infer the correct service provider that would be passed into the instantiator for the given `TServiceName` from `TContainer`.
* ```ts
* const container = fluxject()
* .register(m => m.singleton({ myService: MyService }));
* class MyService {
* constructor(services: InferServiceProvider<typeof container, "myService">) { }
* }
* ```
* @template {Container} TContainer
* Container that holds the service to infer the provider for.
* @template {keyof Types.InferRegistrationsFromContainer<TContainer>} TServiceName
* The name of the service that is using this service provider.
* @typedef {Types.InferRegistrationsFromContainer<TContainer>[TServiceName] extends Types.Registration<*, "scoped">
* ? Types.Widen<Omit<(FluxjectScopedServiceProvider & Types.InferInstanceTypes<Types.InferRegistrationsFromContainer<TContainer>>), TServiceName|"dispose">>
* : Types.Widen<Omit<(FluxjectHostServiceProvider<Types.InferRegistrationsFromContainer<TContainer>> & Types.InferInstanceTypes<Types.InferRegistrationsFromContainer<TContainer>, "singleton"|"transient">), TServiceName|"createScope"|"dispose">>
* } InferServiceProvider
*/
/**
* Infer the Host Service Provider type that would be returned from a container's `prepare` method.
* @template {Container} TContainer
* Container to infer the host service provider from
* @typedef {ReturnType<HostServiceProvider<TContainer>['createScope']>} ScopedServiceProvider
*/
/**
* Infer the Scoped Service Provider type that would be returned from a `HostServiceProvider`'s `createScope` method.
* @template {Container} TContainer
* Container to infer the scoped service provider from
* @typedef {ReturnType<TContainer['prepare']>} HostServiceProvider
*/
/**
* Can be used to infer the abstract type of a class or factory function.
*
* This would ensure that the type of service inferred by container injection is
* of a parent type of the actual service.
* ```ts
* import { fluxject } from "fluxject";
* import type { Abstract } from "fluxject";
*
* interface IService {
* test: string;
* }
*
* class MyService {
* hello = "hello";
* world = "world";
* }
*
* const provider = fluxject()
* .register(m => m.singleton({ myService: MyService as Abstract<IService> }))
* .prepare();
*
* provider.myService.hello; // OK
* provider.myService.world; // TypeScript Error: Property 'world' does not exist on type 'IService'
* ```
* @template TInterface
* @typedef {Types.Instantiator<TInterface>} Abstract
*/
/**
* Can be used to infer the abstract type of a class or factory function.
*
* This would ensure that the type of service inferred by container injection is
* of a parent type of the actual service.
*
* __NOTE:__ This is a type-only function and does not have any runtime effect.
* ```ts
* import { fluxject, abstract } from "fluxject";
*
* interface IService {
* test: string;
* }
*
* class MyService {
* hello = "hello";
* world = "world";
* }
*
* const provider = fluxject()
* .register(m => m.singleton({ myService: abstract<IService>(MyService)}))
* .prepare();
*
* provider.myService.hello; // OK
* provider.myService.world;
* // TypeScript Error: Property 'world' does not exist on type 'IService'
* ```
* @template TInterface
* @template [TImplementedType=TInterface]
* @param {Types.Instantiator<TImplementedType>} registration
* @returns {Abstract<TInterface>}
*/
export function abstract<TInterface, TImplementedType = TInterface>(registration: Types.Instantiator<TImplementedType>): Abstract<TInterface>;
export { Container };
/**
* Infer the correct service provider that would be passed into the instantiator for the given `TServiceName` from `TContainer`.
* ```ts
* const container = fluxject()
* .register(m => m.singleton({ myService: MyService }));
* class MyService {
* constructor(services: InferServiceProvider<typeof container, "myService">) { }
* }
* ```
*/
export type InferServiceProvider<TContainer extends Container<any>, TServiceName extends keyof Types.InferRegistrationsFromContainer<TContainer>> = Types.InferRegistrationsFromContainer<TContainer>[TServiceName] extends Types.Registration<any, "scoped"> ? Types.Widen<Omit<(FluxjectScopedServiceProvider<any> & Types.InferInstanceTypes<Types.InferRegistrationsFromContainer<TContainer>>), TServiceName | "dispose">> : Types.Widen<Omit<(FluxjectHostServiceProvider<Types.InferRegistrationsFromContainer<TContainer>> & Types.InferInstanceTypes<Types.InferRegistrationsFromContainer<TContainer>, "singleton" | "transient">), TServiceName | "createScope" | "dispose">>;
/**
* Infer the Host Service Provider type that would be returned from a container's `prepare` method.
*/
export type ScopedServiceProvider<TContainer extends Container<any>> = ReturnType<HostServiceProvider<TContainer>["createScope"]>;
/**
* Infer the Scoped Service Provider type that would be returned from a `HostServiceProvider`'s `createScope` method.
*/
export type HostServiceProvider<TContainer extends Container<any>> = ReturnType<TContainer["prepare"]>;
/**
* Can be used to infer the abstract type of a class or factory function.
*
* This would ensure that the type of service inferred by container injection is
* of a parent type of the actual service.
* ```ts
* import { fluxject } from "fluxject";
* import type { Abstract } from "fluxject";
*
* interface IService {
* test: string;
* }
*
* class MyService {
* hello = "hello";
* world = "world";
* }
*
* const provider = fluxject()
* .register(m => m.singleton({ myService: MyService as Abstract<IService> }))
* .prepare();
*
* provider.myService.hello; // OK
* provider.myService.world; // TypeScript Error: Property 'world' does not exist on type 'IService'
* ```
*/
export type Abstract<TInterface> = Types.Instantiator<TInterface>;
export type ServiceKey<TContainer extends Container<any>> = keyof Types.InferRegistrationsFromContainer<TContainer>;
export type DisposableService<T> = T & Disposable;
export type AsyncDisposableService<T> = T & AsyncDisposable;
import { Container } from "./container.js";
import type * as Types from "./types.js";
import { FluxjectScopedServiceProvider } from "./provider.js";
import { FluxjectHostServiceProvider } from "./provider.js";