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.lifecycle(object, optional): Lifecycle options for extending lifecycle methods from the base class:extendPostConstructMethods(boolean, defaults to true): If true, post-construct methods from the base class will be extended.extendPreDestroyMethods(boolean, defaults to true): If true, pre-destroy methods from the base class will be extended.
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);
injectFromHierarchyâ
Decorator used to inject dependencies from the full class hierarchy (all ancestors). The injection is applied top-down (from the farthest ancestor to the closest base).
Parametersâ
The injectFromHierarchy decorator accepts an optional InjectFromHierarchyOptions object with the following properties:
extendConstructorArguments(boolean, defaults to true): If true, constructor arguments from all ancestors will be injected.extendProperties(boolean, defaults to true): If true, properties from all ancestors will be injected.lifecycle(object, optional): Lifecycle options for extending lifecycle methods from the hierarchy:extendPostConstructMethods(boolean, defaults to true): If true, post-construct methods from all ancestors will be extended.extendPreDestroyMethods(boolean, defaults to true): If true, pre-destroy methods from all ancestors will be extended.
Example: decorating hierarchy constructor argumentsâ
type Weapon = unknown;
@injectable()
abstract class BaseSoldier {
public weapon: Weapon;
constructor(@inject('Weapon') weapon: Weapon) {
this.weapon = weapon;
}
}
@injectable()
abstract class IntermediateSoldier extends BaseSoldier {}
@injectable()
@injectFromHierarchy({
extendConstructorArguments: true,
extendProperties: false,
})
class Soldier extends IntermediateSoldier {}
// Returns a soldier with a weapon
const soldier: Soldier = container.get(Soldier);
Example: decorating hierarchy propertiesâ
type Weapon = unknown;
@injectable()
abstract class BaseSoldier {
@inject('Weapon')
public weapon: Weapon;
}
@injectable()
abstract class IntermediateSoldier extends BaseSoldier {}
@injectable()
@injectFromHierarchy({
extendConstructorArguments: false,
extendProperties: true,
})
class Soldier extends IntermediateSoldier {}
// 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');
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;