decorate
Overview
The decorate
function is a utility that allows you to apply decorators programmatically to classes, methods, properties, and constructor parameters without using the decorator syntax (@decorator
). This is particularly useful when:
- You can't modify the original class to add decorators
- You're working with third-party classes
- You want to conditionally apply decorators
- You're in an environment where decorator syntax is not available
API Reference
The decorate
function has multiple overloads to handle different decorator types:
function decorate(
decorators: ClassDecorator | ClassDecorator[],
target: Function
): void;
function decorate(
decorators: ParameterDecorator | ParameterDecorator[],
target: Function,
parameterIndex: number
): void;
function decorate(
decorators: MethodDecorator | PropertyDecorator | MethodDecorator[] | PropertyDecorator[],
target: Function,
property: string | symbol
): void;
export function decorate(
decorators: ParameterDecorator | ParameterDecorator[],
target: Function,
methodName: string | symbol,
parameterIndex: number,
): void;
Examples
Applying ClassDecorator
Use this to apply class-level decorators like @injectable()
:
class Katana implements Weapon {
public readonly damage: number = 10;
}
// Apply @injectable decorator using decorate function
decorate(injectable(), Katana);
const container: Container = new Container();
container.bind<Weapon>('Weapon').to(Katana);
const katana: Weapon = container.get<Weapon>('Weapon');
Applying ParameterDecorator
Use this to apply decorators to constructor parameters like @inject()
:
@injectable()
class Warrior {
private readonly weapon: Weapon;
constructor(weapon: Weapon) {
this.weapon = weapon;
}
public fight(): string {
return `Fighting with weapon damage: ${this.weapon.damage.toString()}`;
}
}
// Apply @inject decorator to constructor parameter using decorate function
decorate(inject('Weapon'), Warrior, 0);
const container: Container = new Container();
container.bind<Weapon>('Weapon').to(Katana);
container.bind<Warrior>('Warrior').to(Warrior);
const warrior: Warrior = container.get<Warrior>('Warrior');
Applying PropertyDecorator
Use this to apply decorators to class properties like @inject()
:
@injectable()
class Warrior {
public weapon!: Weapon;
public fight(): string {
return `Fighting with weapon damage: ${this.weapon.damage.toString()}`;
}
}
// Apply @inject decorator to property using decorate function
decorate(inject('Weapon'), Warrior, 'weapon');
const container: Container = new Container();
container.bind<Weapon>('Weapon').to(Katana);
container.bind<Warrior>('Warrior').to(Warrior);
const warrior: Warrior = container.get<Warrior>('Warrior');
Applying MethodDecorator
Use this to apply decorators to class methods:
@injectable()
class Katana implements Weapon {
private _damage: number = 10;
public get damage(): number {
return this._damage;
}
public improve(): void {
this._damage += 2;
}
}
// Apply @postConstruct decorator to method using decorate function
decorate(postConstruct(), Katana, 'improve');
const container: Container = new Container();
container.bind<Weapon>('Weapon').to(Katana);
const katana: Katana = container.get<Weapon>('Weapon') as Katana;
Use Cases
Working with Third-Party Classes
When you need to make a third-party class injectable but cannot modify its source code:
import { decorate, injectable } from 'inversify';
import { SomeThirdPartyClass } from 'some-library';
// Make the third-party class injectable
decorate(injectable(), SomeThirdPartyClass);
Conditional Decoration
Apply decorators based on runtime conditions:
import { decorate, injectable } from 'inversify';
class MyService {
// Implementation
}
// Conditionally make it injectable
if (process.env.NODE_ENV === 'production') {
decorate(injectable(), MyService);
}
Important Notes
- The
decorate
function must be called before the class is used in container bindings - For ParameterDecorator, the parameter index is 0-based (first parameter = 0, second = 1, etc.)
- You can pass either a single decorator or an array of decorators