Decorators
This section covers Inversify decorators used to provide class metadata.
injectable
Decorator used to set class metadata so containers can receive class-emitted metadata.
It's highly recommended to annotate every class provided as a service with the @injectable
decorator. However, it's not mandatory in every single case.
Parameters
The injectable
decorator accepts an optional scope
parameter that allows you to define the lifecycle of the bound instance:
@injectable('Singleton') // or 'Transient' or 'Request'
class MyService {
// ...
}
Valid scope values are:
'Singleton'
: The container will create only one instance and will return that instance for all requests.'Transient'
: The container will create a new instance for each request.'Request'
: The container will create an instance for each unique request (useful in web applications with request scoping).
When is injectable mandatory?
Whenever class-emitted metadata is expected.
Consider the following sample code:
import { injectable } from 'inversify';
@injectable()
class B {
readonly foo: string = 'foo';
}
@injectable()
class A {
constructor(public readonly b: B) {}
}
A CommonJS transpilation with the emitDecoratorMetadata option enabled might look like this:
Object.defineProperty(exports, "__esModule", { value: true });
const inversify_1 = require("inversify");
let B = class B {
foo = 'foo';
};
B = __decorate([
(0, inversify_1.injectable)()
], B);
let A = class A {
b;
constructor(b) {
this.b = b;
}
};
A = __decorate([
(0, inversify_1.injectable)(),
__metadata("design:paramtypes", [B])
], A);
TypeScript emits class metadata if and only if there are any class decorators applied to the target class. If we remove the @injectable
from A
, the transpiled code looks very different:
Object.defineProperty(exports, "__esModule", { value: true });
class B {
foo = 'foo';
}
class A {
b;
constructor(b) {
this.b = b;
}
}
This time, no class metadata is emitted even if the emitDecoratorMetadata
TypeScript option is enabled, causing trouble at execution time.
inject
Decorator used to establish a relation between a constructor argument or a class property and a service ID.
When resolving an instance of the class, the target constructor argument or property will be resolved in the same way container.get behaves.
Example: decorating a class constructor argument
@injectable()
class Ninja {
constructor(
@inject(weaponServiceId)
public readonly weapon: Weapon,
) {}
}
const container: Container = new Container();
container.bind(Ninja).toSelf();
container.bind(weaponServiceId).to(Katana);
const ninja: Ninja = container.get(Ninja);
// Returns 10
const ninjaWeaponDamage: number = ninja.weapon.damage;
Example: decorating a property
@injectable()
class Ninja {
@inject(weaponServiceId)
public readonly weapon!: Weapon;
}
const container: Container = new Container();
container.bind(Ninja).toSelf();
container.bind(weaponServiceId).to(Katana);
const ninja: Ninja = container.get(Ninja);
// Returns 10
const ninjaWeaponDamage: number = ninja.weapon.damage;
injectFromBase
Decorator used to inject dependencies from a base class. Child class injections take precedence over base class injections.
Parameters
The injectFromBase
decorator accepts an optional InjectFromBaseOptions
object with the following properties:
extendConstructorArguments
(boolean, defaults to true): If true, constructor arguments from the base class will be injected.extendProperties
(boolean, defaults to true): If true, properties from the base class will be injected.
Example: decorating base class constructor arguments
type Weapon = unknown;
@injectable()
abstract class BaseSoldier {
public weapon: Weapon;
constructor(@inject('Weapon') weapon: Weapon) {
this.weapon = weapon;
}
}
@injectable()
@injectFromBase({
extendConstructorArguments: true,
extendProperties: false,
})
class Soldier extends BaseSoldier {}
// Returns a soldier with a weapon
const soldier: Soldier = container.get(Soldier);
Example: decorating base class properties
type Weapon = unknown;
@injectable()
abstract class BaseSoldier {
@inject('Weapon')
public weapon: Weapon;
}
@injectable()
@injectFromBase({
extendConstructorArguments: false,
extendProperties: true,
})
class Soldier extends BaseSoldier {}
// Returns a soldier with a weapon
const soldier: Soldier = container.get(Soldier);
multiInject
Decorator used to establish a relation between a constructor argument or a class property and a service ID.
When resolving an instance of the class, the target constructor argument or property will be resolved in the same way container.getAll behaves.
Parameters
The multiInject
decorator accepts an optional MultiInjectOptions
object with the following properties:
chained
(boolean): Option to enable chained resolution across container hierarchies. Refer to the docs for more information.
Example: decorating a property
@injectable()
class Ninja {
@multiInject(weaponServiceId)
public readonly weapon!: Weapon[];
}
const container: Container = new Container();
container.bind(Ninja).toSelf();
container.bind(weaponServiceId).to(Katana);
const ninja: Ninja = container.get(Ninja);
// Returns 10
const ninjaWeaponDamage: number | undefined = ninja.weapon[0]?.damage;
named
Decorator used to establish a relation between a constructor argument or a class property and a metadata name.
@injectable()
class Ninja {
public katana: Weapon;
public shuriken: Weapon;
constructor(
@inject('Weapon') @named('melee') katana: Weapon,
@inject('Weapon') @named('ranged') shuriken: Weapon,
) {
this.katana = katana;
this.shuriken = shuriken;
}
}
const container: Container = new Container();
container.bind<Weapon>('Weapon').to(Katana).whenNamed('melee');
container.bind<Weapon>('Weapon').to(Shuriken).whenNamed('ranged');
container.bind(Ninja).toSelf();
const ninja: Ninja = container.get(Ninja);
optional
Decorator used to establish that a target constructor argument or property is optional and, therefore, it shall not be resolved if no bindings are found for the associated service ID.
@injectable()
class Ninja {
public katana: Weapon;
public shuriken: Weapon | undefined;
constructor(
@inject('Katana') katana: Weapon,
@inject('Shuriken') @optional() shuriken: Weapon | undefined,
) {
this.katana = katana;
this.shuriken = shuriken;
}
}
const container: Container = new Container();
container.bind<Weapon>('Katana').to(Katana);
container.bind(Ninja).toSelf();
// Returns a "Ninja" instance with a "Katana" katana property and undefined shuriken property.
const ninja: Ninja = container.get(Ninja);
postConstruct
Decorator used to establish an activation handler for the target class. Refer to the docs for more information.
interface Weapon {
damage: number;
}
export class Katana implements Weapon {
#damage: number = 10;
public get damage(): number {
return this.#damage;
}
@postConstruct()
public improve(): void {
this.#damage += 2;
}
}
const container: Container = new Container();
container.bind<Weapon>('Weapon').to(Katana);
// Katana.damage is 12
const katana: Weapon = container.get<Weapon>('Weapon');
preDestroy
Decorator used to establish a deactivation handler for the target class. Refer to the docs for more information.
interface Weapon {
damage: number;
}
class Katana implements Weapon {
readonly #damage: number = 10;
public get damage(): number {
return this.#damage;
}
@preDestroy()
public onDeactivation(): void {
console.log(`Deactivating weapon with damage ${this.damage.toString()}`);
}
}
const container: Container = new Container();
container.bind<Weapon>('Weapon').to(Katana).inSingletonScope();
container.get('Weapon');
await container.unbind('Weapon');
tagged
Decorator used to establish a relation between a constructor argument or a class property and a metadata tag.
@injectable()
class Ninja {
public katana: Weapon;
public shuriken: Weapon;
constructor(
@inject('Weapon') @tagged('weaponKind', 'melee') katana: Weapon,
@inject('Weapon') @tagged('weaponKind', 'ranged') shuriken: Weapon,
) {
this.katana = katana;
this.shuriken = shuriken;
}
}
const container: Container = new Container();
container.bind<Weapon>('Weapon').to(Katana).whenTagged('weaponKind', 'melee');
container
.bind<Weapon>('Weapon')
.to(Shuriken)
.whenTagged('weaponKind', 'ranged');
container.bind(Ninja).toSelf();
const ninja: Ninja = container.get(Ninja);
unmanaged
Decorator used to establish that Inversify should not inject the target constructor argument or property whatsoever.
import { Container, injectable, unmanaged } from 'inversify';
@injectable()
class Base {
public prop: string;
constructor(@unmanaged() arg: string) {
this.prop = arg;
}
}
@injectable()
class Derived extends Base {
constructor() {
super('inherited-value');
}
}
const container: Container = new Container();
container.bind(Derived).toSelf();
const derived: Derived = container.get(Derived);
// Returns 'inherited-value'
const derivedProp: string = derived.prop;