Deninject
A dependency injection module for Deno.
Available at Deno Land: Deninject
Contents
Injector
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);
Decorators
The decorators are responsible for making all the configuration of the dependencies. Below are their definitions:
Transient
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();
}
}
Singleton
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();
}
}
Scope
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);
Token
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");
Inject
To inject a specific instance using a token.
class ClassB {
constructor(@Inject("tokenA", true /*If you want use ignoreType*/) a: ClassA) {}
}
Dynamic Token
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();
}
}
}
Symbols
The Symbols
serve to facilitate the use of scopes and tokens in the application, it is just another option of use.
Scope Symbol
It has the same purpose as the Scope
.
const scopeA = new ScopeSymbol();
@Transient()
@scopeA.apply()
class ClassA {}
Token Symbol
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) {}
}
Setup
This modules requires the following options to be set in the tsconfig:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}