A decorator is a function applied to a class or class member, written with
@. TypeScript has two incompatible forms. Without options, the compiler uses
the standard ECMAScript decorators (5.0). With experimentalDecorators it uses
the legacy form, which older frameworks rely on
(Release
notes 5.0;
Handbook:
Decorators, which covers the legacy form).
Syntax
Decorator:
@ Identifier {. Identifier}
@ Identifier {. Identifier} ( Arguments )
@ ( Expression )
Decorators go on classes and on their methods, accessors and fields. They cannot go on functions, interfaces or other declarations.
A class decorator may go before or after export, but not in both
places.
@register export class A {}
export @register class B {}
const util = { logged };
class C {
@util.logged run() {}
@(util.logged) stop() {}
}
@register function f() {} // error: not valid on a function
With several decorators, the expressions are evaluated from top to bottom, and the decorators are applied from bottom to top, like nested function calls.
Standard decorators
A standard decorator receives the thing being decorated and a context object. It may return a replacement.
Decorator(value, context) => replacement | void
| Applied to | value | Context type | May return |
|---|---|---|---|
| Class | The class | ClassDecoratorContext | A replacement class |
| Method | The method | ClassMethodDecoratorContext | A replacement function |
| Getter | The getter | ClassGetterDecoratorContext | A replacement function |
| Setter | The setter | ClassSetterDecoratorContext | A replacement function |
| Field | undefined | ClassFieldDecoratorContext | A function from the initial value to a new one |
| Auto-accessor | ClassAccessorDecoratorTarget with get and set | ClassAccessorDecoratorContext | ClassAccessorDecoratorResult with get, set and init |
These types are declared in the standard library. The context has these fields:
| Field | Meaning |
|---|---|
kind | The string "class", "method", "getter", "setter", "field" or "accessor" |
name | The name of the member or class |
static, private | Whether the member is static or a #private member (not on a class) |
access | Functions to read or write the member (not on a class) |
addInitializer(fn) | Adds a function to run during construction, or for a class, when it is defined |
metadata | An object shared by the decorators of a class (5.2), exposed as Class[Symbol.metadata] where that symbol exists |
function logged<This, A extends unknown[], R>(
target: (this: This, ...a: A) => R,
context: ClassMethodDecoratorContext<This, (this: This, ...a: A) => R>,
) {
const name = String(context.name);
return function (this: This, ...a: A): R {
console.log("calling", name);
return target.call(this, ...a);
};
}
function double<V extends number>(_: undefined, context: ClassFieldDecoratorContext<unknown, V>) {
return (initial: V) => (initial * 2) as V;
}
class Counter {
@double count = 1;
@logged increment(by: number) { return this.count + by; }
}
Standard decorators cannot go on parameters, and they do not work with
emitDecoratorMetadata.
Legacy decorators
experimentalDecorators turns on the older proposal. The decorator takes
different arguments for each target, and the compiler emits calls to a helper after the
class.
| Applied to | Arguments | Return value |
|---|---|---|
| Class | (constructor) | A replacement class |
| Method | (target, key, descriptor) | A new descriptor |
| Accessor | (target, key, descriptor) | A new descriptor |
| Property | (target, key) | Ignored |
| Parameter | (target, key, index) | Ignored |
For a member, target is the class for a static member and the prototype
for an instance member. Only one of a pair of accessors may be decorated, and the
decorators apply to the combined property.
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
function enumerable(value: boolean) {
return function (target: any, key: string | symbol, descriptor: PropertyDescriptor) {
descriptor.enumerable = value;
};
}
function required(target: object, key: string | symbol | undefined, index: number) {}
@sealed
class Greeter {
@enumerable(false)
greet(@required name: string) { return name; }
}
Decorators are applied in this order: parameter, then method, accessor or property decorators for each instance member; the same for each static member; parameter decorators on the constructor; and finally class decorators.
Decorator metadata
With experimentalDecorators and emitDecoratorMetadata, the
compiler records type information for decorated declarations, which the
reflect-metadata package can read at run time.
| Key | Value |
|---|---|
design:type | The type of the property, or Function for a method |
design:paramtypes | The constructor or method parameter types |
design:returntype | The return type of a method |
Each type is written as a run-time value, such as String or a class. Types
with no run-time value become Object.
__decorate([
enumerable(false),
__param(0, required),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", void 0)
], Greeter.prototype, "greet", null);