Skip to main content
Version: Next

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 { 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.

Arguments

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.

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;