Registration
A ServiceCollection is a list of registrations. Each one says: for this token, build the service this way,
with this lifetime. You fill the collection at startup, then call buildServiceProvider() to get a
ServiceProvider that resolves services.
The five forms
Section titled “The five forms”addSingleton, addScoped and addTransient accept the same arguments. Only the lifetime differs.
const const services: ServiceCollection
services = new new ServiceCollection(): ServiceCollection
Collects service registrations, then builds a
ServiceProvider
.
Modeled on .NET's IServiceCollection.
ServiceCollection();
// 1. A class with no constructor parameters is its own token.const services: ServiceCollection
services.ServiceCollection.addSingleton<Cache>(token: Newable<Cache, []>): ServiceCollection (+4 overloads)
Registers a singleton: one instance for the root provider and all its scopes.
addSingleton(Class) / addSingleton(Class, [deps]): the class is its own token.
addSingleton(token, Implementation, [deps]): deps are checked against the constructor's parameters.
addSingleton(token, (provider) => instance): a factory, may be async.
addSingleton(class Cache
Cache);
// 2. A class is its own token, with one dependency token per constructor parameter.const services: ServiceCollection
services.ServiceCollection.addSingleton<Scheduler, [clock: Clock]>(token: Newable<Scheduler, [clock: Clock]>, dependencies: readonly [clock: Token<Clock>]): ServiceCollection (+4 overloads)
Registers a singleton: one instance for the root provider and all its scopes.
addSingleton(Class) / addSingleton(Class, [deps]): the class is its own token.
addSingleton(token, Implementation, [deps]): deps are checked against the constructor's parameters.
addSingleton(token, (provider) => instance): a factory, may be async.
addSingleton(class Scheduler
Scheduler, [const Clock: InjectionToken<Clock>
Clock]);
// 3. A token and an implementation class.const services: ServiceCollection
services.ServiceCollection.addSingleton<Clock>(token: Token<Clock>, implementation: Newable<NoInfer<Clock>, []>): ServiceCollection (+4 overloads)
Registers a singleton: one instance for the root provider and all its scopes.
addSingleton(Class) / addSingleton(Class, [deps]): the class is its own token.
addSingleton(token, Implementation, [deps]): deps are checked against the constructor's parameters.
addSingleton(token, (provider) => instance): a factory, may be async.
addSingleton(const Clock: InjectionToken<Clock>
Clock, class SystemClock
SystemClock);
// 4. A token, an implementation and its dependencies.const const Audit: InjectionToken<AuditLog>
Audit = createToken<AuditLog>(name: string): InjectionToken<AuditLog>
Creates a typed token.
createToken<class AuditLog
AuditLog>('Audit');const services: ServiceCollection
services.ServiceCollection.addSingleton<AuditLog, [clock: Clock, cache: Cache]>(token: Token<AuditLog>, implementation: Newable<NoInfer<AuditLog>, [clock: Clock, cache: Cache]>, dependencies: readonly [clock: Token<Clock>, cache: Token<Cache>]): ServiceCollection (+4 overloads)
Registers a singleton: one instance for the root provider and all its scopes.
addSingleton(Class) / addSingleton(Class, [deps]): the class is its own token.
addSingleton(token, Implementation, [deps]): deps are checked against the constructor's parameters.
addSingleton(token, (provider) => instance): a factory, may be async.
addSingleton(const Audit: InjectionToken<AuditLog>
Audit, class AuditLog
AuditLog, [const Clock: InjectionToken<Clock>
Clock, class Cache
Cache]);
// 5. A token and a factory: any function that returns the instance (or a promise of it).const services: ServiceCollection
services.ServiceCollection.addSingleton<unknown>(token: Token<unknown>, factory: ServiceFactory<unknown>): ServiceCollection (+4 overloads)
Registers a singleton: one instance for the root provider and all its scopes.
addSingleton(Class) / addSingleton(Class, [deps]): the class is its own token.
addSingleton(token, Implementation, [deps]): deps are checked against the constructor's parameters.
addSingleton(token, (provider) => instance): a factory, may be async.
addSingleton('startedAt', () => new var Date: DateConstructornew () => Date (+3 overloads)
Date());Every method returns the collection, so calls chain.
| Form | Use it when |
|---|---|
add*(Class) / add*(Class, [deps]) |
The concrete class is what callers ask for. |
add*(token, Impl) / add*(token, Impl, [deps]) |
Callers depend on an interface or base class; you pick the implementation here. |
add*(token, factory) |
Construction needs logic: config, async setup, a third-party constructor. See Factories. |
addValue(token, value) |
You already have the object: config, a constant, an instance built elsewhere. |
Classes versus factories
Section titled “Classes versus factories”The second argument is treated as an implementation only if it is written with class syntax.
Any other function, arrow or function, is a factory and is called as factory(provider, key).
const const services: ServiceCollection
services = new new ServiceCollection(): ServiceCollection
Collects service registrations, then builds a
ServiceProvider
.
Modeled on .NET's IServiceCollection.
ServiceCollection();
const services: ServiceCollection
services.ServiceCollection.addSingleton<Database>(token: Token<Database>, factory: ServiceFactory<NoInfer<Database>>): ServiceCollection (+4 overloads)
Registers a singleton: one instance for the root provider and all its scopes.
addSingleton(Class) / addSingleton(Class, [deps]): the class is its own token.
addSingleton(token, Implementation, [deps]): deps are checked against the constructor's parameters.
addSingleton(token, (provider) => instance): a factory, may be async.
addSingleton(class Database
Database, (provider: ServiceProvider
provider) => new constructor Database(url: string): Database
Database(function readDatabaseUrl(): string
readDatabaseUrl()));Factories get the provider, so they can resolve other services. They are covered in Factories.
Values
Section titled “Values”addValue registers something that already exists. Any value is allowed, null and undefined included.
Values behave like singletons but are never constructed and never disposed: the container
didn’t create them, so it doesn’t own them.
interface interface Config
Config { Config.port: number
port: number; Config.dbUrl: string
dbUrl: string;}const const Config: InjectionToken<Config>
Config = createToken<Config>(name: string): InjectionToken<Config>
Creates a typed token.
createToken<interface Config
Config>('Config');
const const services: ServiceCollection
services = new new ServiceCollection(): ServiceCollection
Collects service registrations, then builds a
ServiceProvider
.
Modeled on .NET's IServiceCollection.
ServiceCollection().ServiceCollection.addValue<Config>(token: Token<Config>, value: NoInfer<Config>): ServiceCollection
Registers an existing value (config, constants, an instance you built). Any value works, null and undefined included. Never disposed by the container.
addValue(const Config: InjectionToken<Config>
Config, { Config.port: number
port: 8080, Config.dbUrl: string
dbUrl: 'postgres://localhost/app' });Registering a token twice
Section titled “Registering a token twice”Registrations don’t overwrite each other: a second add* for the same token is added to the list.
getService and getRequiredService return the last one; getServices returns all of them in order.
Each registration has its own instance, even when two registrations use the same class.
const const Notifier: InjectionToken<Notifier>
Notifier = createToken<Notifier>(name: string): InjectionToken<Notifier>
Creates a typed token.
createToken<interface Notifier
Notifier>('Notifier');
const const provider: ServiceProvider
provider = new new ServiceCollection(): ServiceCollection
Collects service registrations, then builds a
ServiceProvider
.
Modeled on .NET's IServiceCollection.
ServiceCollection() .ServiceCollection.addSingleton<Notifier>(token: Token<Notifier>, implementation: Newable<NoInfer<Notifier>, []>): ServiceCollection (+4 overloads)
Registers a singleton: one instance for the root provider and all its scopes.
addSingleton(Class) / addSingleton(Class, [deps]): the class is its own token.
addSingleton(token, Implementation, [deps]): deps are checked against the constructor's parameters.
addSingleton(token, (provider) => instance): a factory, may be async.
addSingleton(const Notifier: InjectionToken<Notifier>
Notifier, class EmailNotifier
EmailNotifier) .ServiceCollection.addSingleton<Notifier>(token: Token<Notifier>, implementation: Newable<NoInfer<Notifier>, []>): ServiceCollection (+4 overloads)
Registers a singleton: one instance for the root provider and all its scopes.
addSingleton(Class) / addSingleton(Class, [deps]): the class is its own token.
addSingleton(token, Implementation, [deps]): deps are checked against the constructor's parameters.
addSingleton(token, (provider) => instance): a factory, may be async.
addSingleton(const Notifier: InjectionToken<Notifier>
Notifier, class SmsNotifier
SmsNotifier) .ServiceCollection.buildServiceProvider(options?: BuildOptions): ServiceProvider
Builds the provider. Registrations are copied, so changing this collection afterwards
does not affect providers already built.
buildServiceProvider();
await const provider: ServiceProvider
provider.ServiceProvider.getRequiredService<Notifier>(token: Token<Notifier>): Promise<Notifier>
Resolves token, throwing ServiceNotFoundError if it is not registered.
getRequiredService(const Notifier: InjectionToken<Notifier>
Notifier); // SmsNotifier (last wins)await const provider: ServiceProvider
provider.ServiceProvider.getServices<Notifier>(token: Token<Notifier>): Promise<Notifier[]>
Resolves every registration of token, in registration order. Empty if none.
getServices(const Notifier: InjectionToken<Notifier>
Notifier); // [EmailNotifier, SmsNotifier]That is how plugins and handlers are collected; see Multiple implementations.
To register only if nothing is there yet, use tryAddSingleton / tryAddScoped / tryAddTransient:
// A library sets a default; the app's own registration, made earlier, wins.const services: ServiceCollection
services.ServiceCollection.tryAddSingleton<Notifier>(token: Token<Notifier>, implementation: Newable<NoInfer<Notifier>, []>): ServiceCollection (+4 overloads)
Like
addSingleton
, but does nothing if token is already registered.
tryAddSingleton(const Notifier: InjectionToken<Notifier>
Notifier, class ConsoleNotifier
ConsoleNotifier);remove, replace and has edit the collection before it is built: see Service management.
Keyed registrations (addKeyedSingleton and friends) are a separate namespace: see Keyed services.
Building the provider
Section titled “Building the provider”const const provider: ServiceProvider
provider = const services: ServiceCollection
services.ServiceCollection.buildServiceProvider(options?: BuildOptions): ServiceProvider
Builds the provider. Registrations are copied, so changing this collection afterwards
does not affect providers already built.
buildServiceProvider({ BuildOptions.validateScopes?: boolean | undefined
Throw a ScopeError when a scoped service is resolved from the root provider
or captured by a singleton. Recommended in development.
validateScopes: true, // scoped services can't leak into singletons or the root BuildOptions.validateOnBuild?: boolean | undefined
Check every registration up front (missing dependencies, captive scoped services) and throw a ValidationError.
validateOnBuild: true, // every dependency must be registered});buildServiceProvider copies the registrations. Changing the collection afterwards does not affect providers
already built, so you can build a provider, tweak the collection and build a second one (handy in tests).
Both options are off by default; see Validation.