Skip to content
Buy me a coffee

Tokens

A token is the key a service is registered and resolved under. The container keeps registrations in a Map, so tokens are compared by identity: the same class object, the same InjectionToken instance, the same symbol, an equal string.

type Token<T> = string | symbol | InjectionToken<T> | AbstractNewable<T>;
Token Typed on resolve Best for
A class Yes Concrete services callers depend on directly
An abstract class Yes A base type with several implementations
createToken<T>('Name') Yes Interfaces, values, config, functions
A symbol Only with <T> Existing code that already uses symbols
A string Only with <T> Quick scripts, JavaScript projects

A class is its own token. Resolving it returns an instance of that class, with no generic argument.

class
class Clock
Clock
{
Clock.now(): number
now
() {
return
var Date: DateConstructor

Enables basic storage and retrieval of dates and times.

Date
.
DateConstructor.now(): number

Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC).

now
();
}
}
const
const provider: ServiceProvider
provider
= new
new ServiceCollection(): ServiceCollection

Collects service registrations, then builds a

ServiceProvider

. Modeled on .NET's IServiceCollection.

@example

const services = new ServiceCollection();
services.addSingleton(Logger, ConsoleLogger);
services.addScoped(UserService, [Logger]);
const provider = services.buildServiceProvider({ validateScopes: true });

ServiceCollection
().
ServiceCollection.addSingleton<Clock>(token: Newable<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 Clock
Clock
).
ServiceCollection.buildServiceProvider(options?: BuildOptions): ServiceProvider

Builds the provider. Registrations are copied, so changing this collection afterwards does not affect providers already built.

buildServiceProvider
();
const
const clock: Clock
clock
= await
const provider: ServiceProvider
provider
.
ServiceProvider.getRequiredService<Clock>(token: Token<Clock>): Promise<Clock>

Resolves token, throwing ServiceNotFoundError if it is not registered.

getRequiredService
(
class Clock
Clock
); // Clock

An abstract class can’t be instantiated, but it exists at runtime, so it works as a token for its subclasses. It is the closest thing to an interface that survives compilation.

abstract class
class PaymentGateway
PaymentGateway
{
abstract
PaymentGateway.charge(cents: number): Promise<string>
charge
(
cents: number
cents
: number):
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<string>;
}
class
class StripeGateway
StripeGateway
extends
class PaymentGateway
PaymentGateway
{
async
StripeGateway.charge(cents: number): Promise<string>
charge
(
cents: number
cents
: number) {
return `ch_${
cents: number
cents
}`;
}
}
const
const provider: ServiceProvider
provider
= new
new ServiceCollection(): ServiceCollection

Collects service registrations, then builds a

ServiceProvider

. Modeled on .NET's IServiceCollection.

@example

const services = new ServiceCollection();
services.addSingleton(Logger, ConsoleLogger);
services.addScoped(UserService, [Logger]);
const provider = services.buildServiceProvider({ validateScopes: true });

ServiceCollection
().
ServiceCollection.addSingleton<PaymentGateway>(token: Token<PaymentGateway>, implementation: Newable<NoInfer<PaymentGateway>, []>): 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 PaymentGateway
PaymentGateway
,
class StripeGateway
StripeGateway
).
ServiceCollection.buildServiceProvider(options?: BuildOptions): ServiceProvider

Builds the provider. Registrations are copied, so changing this collection afterwards does not affect providers already built.

buildServiceProvider
();
const
const gateway: PaymentGateway
gateway
= await
const provider: ServiceProvider
provider
.
ServiceProvider.getRequiredService<PaymentGateway>(token: Token<PaymentGateway>): Promise<PaymentGateway>

Resolves token, throwing ServiceNotFoundError if it is not registered.

getRequiredService
(
class PaymentGateway
PaymentGateway
); // PaymentGateway

Interfaces and type aliases vanish at runtime. createToken<T>(name) creates an InjectionToken<T>: a runtime object that carries T in its type. Resolving it infers T.

interface
interface Logger
Logger
{
Logger.log(message: string): void
log
(
message: string
message
: string): void;
}
export const
const Logger: InjectionToken<Logger>
Logger
=
createToken<Logger>(name: string): InjectionToken<Logger>

Creates a typed token.

@example

interface Logger { log(msg: string): void }
const Logger = createToken<Logger>('Logger');
services.addSingleton(Logger, ConsoleLogger);
const logger = await provider.getRequiredService(Logger); // typed as Logger

createToken
<
interface Logger
Logger
>('Logger');
const
const provider: ServiceProvider
provider
= new
new ServiceCollection(): ServiceCollection

Collects service registrations, then builds a

ServiceProvider

. Modeled on .NET's IServiceCollection.

@example

const services = new ServiceCollection();
services.addSingleton(Logger, ConsoleLogger);
services.addScoped(UserService, [Logger]);
const provider = services.buildServiceProvider({ validateScopes: true });

ServiceCollection
()
.
ServiceCollection.addSingleton<Logger>(token: Token<Logger>, factory: ServiceFactory<NoInfer<Logger>>): 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 Logger: InjectionToken<Logger>
Logger
, () => ({
Logger.log(message: string): void
log
: (
m: string
m
: string) =>
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@see ― source

console
.
Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@since ― v0.1.100

log
(
m: string
m
) }))
.
ServiceCollection.buildServiceProvider(options?: BuildOptions): ServiceProvider

Builds the provider. Registrations are copied, so changing this collection afterwards does not affect providers already built.

buildServiceProvider
();
const logger = await
const provider: ServiceProvider
provider
.
ServiceProvider.getRequiredService<Logger>(token: Token<Logger>): Promise<Logger>

Resolves token, throwing ServiceNotFoundError if it is not registered.

getRequiredService
(
const Logger: InjectionToken<Logger>
Logger
);
const logger: Logger

The name is only used in error messages and diagnostics. Identity is the object: two createToken('Logger') calls create two different tokens. Create each token once, export it, and import it wherever it is needed.

Typed tokens also suit values that aren’t objects:

const
const ApiUrl: InjectionToken<string>
ApiUrl
=
createToken<string>(name: string): InjectionToken<string>

Creates a typed token.

@example

interface Logger { log(msg: string): void }
const Logger = createToken<Logger>('Logger');
services.addSingleton(Logger, ConsoleLogger);
const logger = await provider.getRequiredService(Logger); // typed as Logger

createToken
<string>('ApiUrl');
const
const Now: InjectionToken<() => Date>
Now
=
createToken<() => Date>(name: string): InjectionToken<() => Date>

Creates a typed token.

@example

interface Logger { log(msg: string): void }
const Logger = createToken<Logger>('Logger');
services.addSingleton(Logger, ConsoleLogger);
const logger = await provider.getRequiredService(Logger); // typed as Logger

createToken
<() =>
interface Date

Enables basic storage and retrieval of dates and times.

Date
>('Now');
const
const provider: ServiceProvider
provider
= new
new ServiceCollection(): ServiceCollection

Collects service registrations, then builds a

ServiceProvider

. Modeled on .NET's IServiceCollection.

@example

const services = new ServiceCollection();
services.addSingleton(Logger, ConsoleLogger);
services.addScoped(UserService, [Logger]);
const provider = services.buildServiceProvider({ validateScopes: true });

ServiceCollection
()
.
ServiceCollection.addValue<string>(token: Token<string>, value: string): 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 ApiUrl: InjectionToken<string>
ApiUrl
, 'https://api.example.com')
.
ServiceCollection.addValue<() => Date>(token: Token<() => Date>, value: NoInfer<() => Date>): 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 Now: InjectionToken<() => Date>
Now
, () => new
var Date: DateConstructor
new () => Date (+3 overloads)
Date
())
.
ServiceCollection.buildServiceProvider(options?: BuildOptions): ServiceProvider

Builds the provider. Registrations are copied, so changing this collection afterwards does not affect providers already built.

buildServiceProvider
();
const
const url: string
url
= await
const provider: ServiceProvider
provider
.
ServiceProvider.getRequiredService<string>(token: Token<string>): Promise<string>

Resolves token, throwing ServiceNotFoundError if it is not registered.

getRequiredService
(
const ApiUrl: InjectionToken<string>
ApiUrl
); // string

Strings and symbols carry no type. Resolving one without a type argument gives unknown; pass the type explicitly:

interface
interface Mailer
Mailer
{
Mailer.send(to: string): Promise<void>
send
(
to: string
to
: string):
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<void>;
}
const
const MAILER: typeof MAILER
MAILER
=
var Symbol: SymbolConstructor
(description?: string | number) => symbol

Returns a new unique Symbol value.

@param ― description Description of the new Symbol object.

Symbol
('Mailer');
const
const provider: ServiceProvider
provider
= new
new ServiceCollection(): ServiceCollection

Collects service registrations, then builds a

ServiceProvider

. Modeled on .NET's IServiceCollection.

@example

const services = new ServiceCollection();
services.addSingleton(Logger, ConsoleLogger);
services.addScoped(UserService, [Logger]);
const provider = services.buildServiceProvider({ validateScopes: true });

ServiceCollection
()
.
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
(
const MAILER: typeof MAILER
MAILER
, () => ({
send: (to: string) => Promise<void>
send
: async (
to: string
to
: string) => {} }))
.
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
('mailer', () => ({
send: (to: string) => Promise<void>
send
: async (
to: string
to
: string) => {} }))
.
ServiceCollection.buildServiceProvider(options?: BuildOptions): ServiceProvider

Builds the provider. Registrations are copied, so changing this collection afterwards does not affect providers already built.

buildServiceProvider
();
const
const a: Mailer
a
= await
const provider: ServiceProvider
provider
.
ServiceProvider.getRequiredService<Mailer>(token: Token<Mailer>): Promise<Mailer>

Resolves token, throwing ServiceNotFoundError if it is not registered.

getRequiredService
<
interface Mailer
Mailer
>(
const MAILER: typeof MAILER
MAILER
); // Mailer
const
const b: Mailer
b
= await
const provider: ServiceProvider
provider
.
ServiceProvider.getRequiredService<Mailer>(token: Token<Mailer>): Promise<Mailer>

Resolves token, throwing ServiceNotFoundError if it is not registered.

getRequiredService
<
interface Mailer
Mailer
>('mailer'); // Mailer

Nothing checks that the type you pass matches what was registered. A typo in a string token is only found at runtime. Prefer createToken in TypeScript code.

Errors and diagnostics print tokens with tokenName(token): the class name, the InjectionToken name, the string itself, or Symbol(description). Give symbols a description and tokens a meaningful name; it is what you will read in a ServiceNotFoundError at 3 a.m.