Skip to main content
Version: 6.x

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.

When is injectable mandatory?

Whenever class-emitted metadata is expected.

Consider the following sample code:

import 'reflect-metadata';
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 });
require("reflect-metadata");
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 });
require("reflect-metadata");
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;

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 with the enforceBindingConstraints flag enabled.

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).whenTargetNamed('melee');
container.bind<Weapon>('Weapon').to(Shuriken).whenTargetNamed('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;
}

export 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');

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)
.whenTargetTagged('weaponKind', 'melee');
container
.bind<Weapon>('Weapon')
.to(Shuriken)
.whenTargetTagged('weaponKind', 'ranged');
container.bind(Ninja).toSelf();

const ninja: Ninja = container.get(Ninja);

targetName

Decorator used to establish a relation between a constructor argument or a class property name at design time.

Bundlers might minify code, altering class property names. This decorator keeps track of the original name.

This property is kept in the name property of the target request in a binding constraint.

@injectable()
class Ninja implements Ninja {
public katana: Weapon;
public shuriken: Weapon;
constructor(
@inject("Weapon") @targetName("katana") katana: Weapon,
@inject("Weapon") @targetName("shuriken") shuriken: Weapon
) {
this.katana = katana;
this.shuriken = shuriken;
}
}

container.bind<Weapon>("Weapon").to(Katana).when((request: interfaces.Request) => {
return request.target.name.equals("katana");
});

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();

const derived: Derived = container.resolve(Derived);

// Returns 'inherited-value'
const derivedProp: string = derived.prop;