Simple generic dependency injection library written in TypeScript (but also works with plain JavaScript).
Some features:
There are some features which are intentionally not supported:
Install the library as a dependency in your project:
npm install @kayahr/cdi
When using decorators then a typical simple use case can look like this:
import { Context, injectable } from "@kayahr/cdi";
@injectable
export class MathService {
public add(a: number, b: number): number {
return a + b;
}
}
@injectable({ inject: [ MathService ] })
export class Component {
public constructor(
private readonly mathService: MathService
) {}
public run(): void {
console.log(this.mathService.add(1, 2));
}
}
// Boot strap
const context = Context.getActive();
context.getSync(Component).run();
This registers a MathService
singleton in the active dependency injection context and a Component
class which depends on it. Then in the boot strap code it fetches the singleton instance of Component
from the context and runs it.
Note that the types of dependencies must explicitly be specified in the injectable
decorator (only if there are dependencies). This is because up to now the new ECMAScript decorators have no support for type reflection metadata or parameter decorators. As soon as this changes, the inject
option will become optional.
You may wonder why context.getSync()
is used instead of context.get()
. This is because all dependencies can be asynchronous (more on that later), so get
may return a Promise and the boot strap code would need to handle this. getSync
on the other hand always returns a synchronous value and throws an error when an asynchronous dependency has been encountered. So when you know that all involved dependencies are synchronous then getSync
is easier to use. There is also a getAsync
method which always returns a promise, if you prefer this.
Instead of using decorators you can also fetch the active dependency injection context and add the classes manually:
import { Context } from "@kayahr/cdi";
const context = Context.getActive();
export class MathService {
public add(a: number, b: number): number {
return a + b;
}
}
context.setClass(MathService);
export class Component {
public constructor(
private readonly mathService: MathService
) {}
public run(): void {
console.log(this.mathService.add(1, 2));
}
}
context.setClass(Component, { inject: [ MathService ] });
// Boot strap
context.getSync(Component).run();
The injectable
decorator internally uses the same methods which you can also use manually, like in this example, so the functionality is identical.