Decorator Composition
In this page:
Applying Multiple Decorators
You can stack several decorators on the same class member, one per line, and TypeScript applies each of them to progressively wrap or transform the target in sequence.
Example: Applying Multiple Decorators
function a(target: any, ctx: ClassMethodDecoratorContext) { console.log("a applied"); }
function b(target: any, ctx: ClassMethodDecoratorContext) { console.log("b applied"); }
class Example {
@a
@b
run() {}
}
new Example();
Evaluation and Application Order
Decorator factories evaluate top-to-bottom in the order they're written, but the decorators they return are actually applied bottom-to-top — understanding this two-phase order is essential to predicting the final behavior.
Example: Evaluation and Application Order
function factory(name: string) {
console.log("Evaluating factory", name);
return function (target: any, ctx: ClassMethodDecoratorContext) {
console.log("Applying decorator", name);
};
}
class Example {
@factory("top")
@factory("bottom")
run() {}
}
new Example();
Composing Method Wrappers
Composing decorators that each wrap a method (like one for logging and one for timing) lets you layer independent method behaviors without merging their logic into a single, harder-to-maintain decorator.
Example: Composing Method Wrappers
function withLogging(target: any, ctx: ClassMethodDecoratorContext) {
return function (this: any, ...args: any[]) {
console.log("log:", String(ctx.name));
return target.call(this, ...args);
};
}
function withTiming(target: any, ctx: ClassMethodDecoratorContext) {
return function (this: any, ...args: any[]) {
console.log("timing started");
return target.call(this, ...args);
};
}
class Service {
@withLogging
@withTiming
run() {
return "done";
}
}
new Service().run();
Combining Different Behaviors
Different decorators can be combined to add unrelated capabilities to the same class member — for example pairing a validation decorator with a logging decorator — as long as each one still returns a compatible descriptor.
Example: Combining Different Behaviors
function validate(target: any, ctx: ClassMethodDecoratorContext) {
return function (this: any, ...args: any[]) {
console.log("validating");
return target.call(this, ...args);
};
}
function log(target: any, ctx: ClassMethodDecoratorContext) {
return function (this: any, ...args: any[]) {
console.log("logging");
return target.call(this, ...args);
};
}
class Form {
@validate
@log
submit() {
return "submitted";
}
}
new Form().submit();
Order Changes Results
Because application order runs bottom-to-top, reordering two decorators on the same member can change which one sees the other's modified output first, so swapping their order can genuinely change the result.
Example: Order Changes Results
function addOne(target: any, ctx: ClassMethodDecoratorContext) {
return function (this: any) {
return target.call(this) + 1;
};
}
function double(target: any, ctx: ClassMethodDecoratorContext) {
return function (this: any) {
return target.call(this) * 2;
};
}
class MathOp {
@double
@addOne
compute() {
return 5;
}
}
console.log(new MathOp().compute());
Chapter Quiz — Complete all 7 topics to unlock
0/7 topics done
Complete these topics first: