A simple and intuitive dependency injection library supporting containers chaining.
import { Injector, Token, provider } from 'injectree';
class Greeter {
constructor(private readonly name: string) {}
greet() {
return `Hello ${this.name}!`;
}
}
const NAME = new Token<string>();
const injector = new Injector([
provider(NAME, {value: 'World'}),
provider(Greeter, {deps: [NAME]}),
]);
const greeter = injector.get(Greeter);
greeter.greet(); // returns 'Hello World!'
Provides a value directly.
Example:
const TOKEN = new Token<string>('TOKEN');
const injector = new Injector([
provider(TOKEN, {value: 'Hello!'}),
]);
Provides a value returned form the factory function. Can specify dependencies that are provided to the factory function as parameters.
Example:
const TOKEN = new Token<string>('TOKEN');
const NAME = new Token<string>('NAME');
const injector = new Injector([
provider(TOKEN, {factory: (name: string) => `Hello ${name}!`, deps: [NAME]}),
provider(NAME, {value: 'World'}),
]);
Provides an instance of the class. Can specify dependencies that are provided to the constructor as parameters.
Example:
class Greeter {
constructor(private name: string) {}
greet() {
return `Hello ${this.name}!`;
}
}
const NAME = new Token<string>('NAME');
const injector = new Injector([
provider(Greeter, {class: Greeter, deps: [NAME]}),
provider(NAME, {value: 'World'}),
]);
// or
const injector = new Injector([
provider(Greeter, {deps: [NAME]}), // class derived from the provider token
provider(NAME, {value: 'World'}),
]);
Provides a value/instance provided under another token.
Example:
const TOKEN = new Token<string>('TOKEN');
const OTHER_TOKEN = new Token<string>('OTHER_TOKEN');
const injector = new Injector([
provider(OTHER_TOKEN, {value: 'Hello!'}),
provider(TOKEN, {token: OTHER_TOKEN}),
]);
The defaultProvider
function allows for specifying providers for tokens that are not explicitly defined on any injector.
Note that the default provider needs to be defined before the injector is created.
Example:
class Service {
getValue() {
return 42;
}
}
defaultProvider(Service, {deps: []});
// later
const injector = new Injector();
const service = injector.get(Service);
service.getValue(); // return 42
A multi token can hold bindings to multiple values and/or instances. Multiple providers can be specified for a single multi token.
Example:
const MULTI_TOKEN = new MultiToken<string>('MULTI_TOKEN');
defaultProvider(MULTI_TOKEN, {value: 'default'});
const injector = new Injector([
provider(MULTI_TOKEN, {value: 'first'}),
provider(MULTI_TOKEN, {value: 'second'}),
]);
injector.get(MULTI_TOKEN); // returns ['default', 'first', 'second']
An injector can specify its parent injector when it's created. If a provider is not found in the injector and the parent injector is specified, the search will be continued in the parent injector. In case of multi token, values and/or instances will be returned also from the parent injector.
Injection options can be used to control the resolution process.
They can be specified on the Injector.get()
method or on the dep
function when declaring dependencies on factory and class providers.
If set to true
, the injector will return undefined
if no provider was defined for the token instead of throwing an error.
Example:
const TOKEN = new Token<string>('TOKEN');
const injector = new Injector();
const value: string | undefined = injector.get(TOKEN, {optional: true});
const TOKEN = new Token<string>('TOKEN');
class Service {
constructor(readonly value: string | undefined) {}
}
const injector = new Injector([
provider(Service, {deps: [dep(TOKEN, {optional: true})]}),
]);
Specifies where to search for the provider.
self-and-ancestors
(default) - search in the current injector or any of it's ancestors,self
- search only in the current injector,ancestors
- search only in ancestor injectors.
An instance is created right after it's resolved using the Injector.get()
method (either directly or as a transitive dependency).
Every successive resolution for the same token will result with the same instance being returned.
An instance remains active until the injector that created it is destroyed.
In case of instances created using the class provider, the [onDestroy]
hook is available to give an instance a chance to clean up after itself.
Example:
import {Injector, provider, onDestroy} from 'injectree';
class Service {
private unsubscribe: () => void;
constructor() {
this.unsubscribe = subscribeToSomeDataSource();
}
[onDestroy]() {
this.unsubscribe();
this.unsubscribe = undefined;
}
}
const injector = new Injector([
provider(Service, {deps: []}),
])
injector.get(Service); // Service created
// later
injector.destroy(); // Service[onDestroy] called