Capítulo 19 de 36
Decorators (@expression) are a meta-programming layer for observing, modifying, or replacing classes and their members at definition time — this reference page documents the older experimental stage-2 implementation (behind experimentalDecorators), distinct from the newer stage-3 decorators shipped natively since TypeScript 5.0.
experimentalDecorators: true in tsconfig.json (or --experimentalDecorators on the CLI) for this stage-2 form.@sealed decorator is just a function; a decorator factory is a function that returns a decorator function, letting you parameterize it — @enumerable(false).(f ∘ g)(x) = f(g(x)).Object.seal(constructor) to lock it against further modification) or return a new constructor to replace the class entirely — if you do the latter, you're responsible for preserving the original prototype chain yourself, since the decorator runtime doesn't do it for you. A class decorator that adds a new property (e.g. via a returned subclass) does not update the TypeScript-visible type of the class — the new member exists at runtime but is invisible to the type-checker.(target, propertyKey, descriptor) — target is the prototype (instance member) or constructor (static member), descriptor is the standard JS PropertyDescriptor. Returning a value from the decorator replaces the descriptor. TypeScript disallows decorating both get and set of one accessor pair separately — decorators apply to whichever accessor is declared first, since both share one underlying PropertyDescriptor.(target, propertyKey) — no PropertyDescriptor and no way to observe/modify the field's initializer, because there's no mechanism to describe an instance property at prototype-definition time. In practice, property decorators can only record metadata about "a property with this name exists" (e.g. via the reflect-metadata library), not intercept reads/writes.(target, memberName, parameterIndex) — can only observe that a parameter at a given position exists (e.g. to record "this parameter is required" as metadata); the return value is ignored. Real enforcement (like actually throwing on a missing required argument) has to happen in a paired method decorator that reads the metadata the parameter decorator recorded.emitDecoratorMetadata + reflect-metadata: an additional experimental compiler option that, combined with the reflect-metadata polyfill library, emits design-time type information (design:type etc.) accessible at runtime — enabling patterns like a @validate setter decorator that checks the assigned value's runtime type against the property's declared TypeScript type.function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class BugReport {
title: string;
constructor(t: string) { this.title = t; }
}
function enumerable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.enumerable = value;
};
}
class Greeter {
@enumerable(false)
greet() { return "hi"; }
}
enumerable(false) returns the actual decorator function) mutating a method's property descriptor.| Decorator kind | Arguments received | Can it replace/mutate? |
|---|---|---|
| Class | constructor | yes — return a new constructor to replace the class |
| Method / Accessor | target, propertyKey, descriptor | yes — return a new descriptor |
| Property | target, propertyKey | no — observation only, no descriptor access |
| Parameter | target, propertyKey, parameterIndex | no — observation only, return value ignored |
experimentalDecorators vs. native TS 5.0+ decorators) before applying patterns from here.reflect-metadata + emitDecoratorMetadata is what unlocks runtime access to a property's declared TypeScript type — without it, decorators only see what you explicitly pass them.