A dependency injection module for Deno.
Available at Deno Land: Deninject
The main feature of this module is the Injector
. This is a class that contains all the dependencies and will be resolvable based on the settings of the injection modules using some specific decorators. Let’s see a simple example:
import { Injector, Singleton, Transient } from "https://deno.land/x/deninject/mod.ts";
class ClassA {
constructor() {
console.log("buildA");
}
}
class ClassB {
constructor(a: ClassA) {
console.log("buildB: ", a);
}
}
class MyModule {
@Singleton()
public buildA(): ClassA {
return new ClassA();
}
@Transient()
public buildB(a: ClassA): ClassB {
return new ClassB(a);
}
}
const injector = new Injector(new MyModule());
const b = injector.get(ClassB);
The decorators are responsible for making all the configuration of the dependencies. Below are their definitions:
The decorator Transient
serves to mark a class or method of a module as a transient, this means that the Injector
will generate a new instance for each request.
As a class:
@Transient()
class ClassA {}
As a module:
class ClassA {}
class MyModule {
@Transient()
public buildA(): ClassA {
return new ClassA();
}
}
The decorator Singleton
serves to mark a class or method of a module as a singleton, this means that the Injector
will generate just one instance and store it in the cache, each request will return the same instance.
As a class:
@Singleton()
class ClassA {}
As a module:
class ClassA {}
class MyModule {
@Singleton()
public buildA(): ClassA {
return new ClassA();
}
}
The decorator Scope
is used to mark a class or method of a module with a specific scope, this is useful for specific configurations for the SubInjectors
. Every SubInjector
works with a specific scope.
As a class:
@Transient()
@Scope("scopeA")
class ClassA {}
As a module:
class ClassA {}
class MyModule {
@Transient()
@Scope("scopeA")
public buildA(): ClassA {
return new ClassA();
}
}
To use a SubInjector
:
const subInjector = injector.sub("scopeA", new MyScopeModule());
const a = subInjector.get(ClassA);
The Token
decorator is used to mark a class or method of a module with a specific token, this is useful for creating different constructors for the same class.
As a class:
@Transient()
@Token("tokenA")
class ClassA {}
As a module:
class ClassA {}
class MyModule {
@Transient()
@Token("tokenA", true /*If you want use ignoreType*/)
public buildA(): ClassA {
return new ClassA();
}
}
To retrieve an instance:
const a = injector.get(ClassA, "tokenA");
To inject a specific instance using a token.
class ClassB {
constructor(@Inject("tokenA", true /*If you want use ignoreType*/) a: ClassA) {}
}
From time to time we need to make a more elaborate logic when instantiating classes, for this purpose DynamicToken
was made, to provide more freedom in the code. Here is an example:
const tokenB = new TokenSymbol();
const tokenC = new TokenSymbol();
class ClassA {}
class ClassB extends ClassA {}
class ClassC extends ClassA {}
class MyModule {
@Transient()
public buildA(@DynamicToken() token: TokenSymbol): ClassA {
if (token == tokenB) {
return new ClassB();
}
else if (token == tokenC) {
return new ClassC();
}
else {
return new ClassA();
}
}
}
The Symbols
serve to facilitate the use of scopes and tokens in the application, it is just another option of use.
It has the same purpose as the Scope
.
const scopeA = new ScopeSymbol();
@Transient()
@scopeA.apply()
class ClassA {}
It has the same purpose as the Token
.
const tokenA = new TokenSymbol(true /*If you want use ignoreType*/);
@Transient()
@tokenA.apply()
class ClassA {}
class ClassB {
constructor(@tokenA.inject() a: ClassA) {}
}
This modules requires the following options to be set in the tsconfig:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
Author: brmcerqueira
Source Code: https://github.com/brmcerqueira/deninject
#deno #node #nodejs #javascript