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.

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;