microsoft/TypeScript
Decorators
TypeScript supports two distinct decorator implementations: the original experimental decorators (TS-specific, predates the TC39 proposal) and Stage-3 decorators (TC39-track, introduced in TypeScript 5.0). Both are alive in the codebase; users opt in to one via --experimentalDecorators.
Where decorators touch the codebase
| Subsystem | File | Role |
|---|---|---|
| Parser | src/compiler/parser.ts |
Decorator productions; Decorator node kind |
| Checker | src/compiler/checker.ts |
Decorator type-checking and metadata generation |
| Legacy transformer | src/compiler/transformers/legacyDecorators.ts |
experimentalDecorators lowering |
| Stage-3 transformer | src/compiler/transformers/esDecorators.ts (~127k effective) |
TC39 Stage-3 decorator lowering |
| Class-fields interaction | src/compiler/transformers/classFields.ts |
Field/decorator initialisation order |
| Factory helpers | src/compiler/factory/emitHelpers.ts |
__decorate, __metadata, __esDecorate, __runInitializers |
Two implementations
Legacy (experimentalDecorators: true)
Original TypeScript decorators — class, method, accessor, property, parameter — applied at evaluation time and able to read/write descriptors. Optionally emit Reflect.metadata for runtime type information when emitDecoratorMetadata: true.
legacyDecorators.ts rewrites:
@inject
class Foo {
@log greet() {}
}into something like:
let Foo = class Foo {
greet() {}
};
Foo = __decorate([inject], Foo);
__decorate([log], Foo.prototype, 'greet', null);The __decorate and __metadata helpers come from emitHelpers.ts.
Stage-3 (default)
Standards-track decorators with a different shape: each decorator receives a value and a context object describing the kind, name, access path, and addInitializer hook. They run during class evaluation in a deterministic order. Field decorators return an initializer-mutating function; accessor/method decorators return a replacement; class decorators return a new class.
esDecorators.ts rewrites:
@inject
class Foo {
@log greet() {}
}into a sequence using __esDecorate and __runInitializers that captures the per-element decorator lists, runs them in spec order, and calls the resulting initializers.
The split between the two transformers is the cleanest signal of how different the two decorator semantics are. Implementing both in one transformer would have been considerably messier; see commit 5b189796 (Jan 19, 2023) — the introduction of esDecorators.ts.
Type checking
The checker validates:
- The decorator's expression type against the position it's applied to (class vs. method vs. property).
- For legacy decorators: the descriptor return type (
undefined | TypedPropertyDescriptor<T>). - For Stage-3 decorators: the
Decorator<Value, Context>shape (thecontextcarrying the rightkind).
The lib.decorators.d.ts (and lib.decorators.legacy.d.ts) files in src/lib/ define the decorator types the checker uses.
emitDecoratorMetadata
When emitDecoratorMetadata: true and experimentalDecorators: true, the legacy transformer emits Reflect.metadata("design:type", T) calls so frameworks like Angular and TypeORM can read parameter and return types at runtime. The metadata payloads are produced from the checker's resolved types via the same type-to-syntax conversion used by declaration emit (expressionToTypeNode.ts).
Diagnostic story
Most decorator diagnostics live in diagnosticMessages.json under codes 1206–1260 (and beyond): "Decorators are not valid here", "A class decorator function should not change the class kind", and so on. The legacy and Stage-3 paths share many messages but each has their own variants for per-implementation invariants.
Tests
Decorator tests live under:
tests/cases/conformance/decorators/— legacy decorators.tests/cases/conformance/esDecorators/— Stage-3 decorators.
Plus several runtime tests under src/testRunner/unittests/evaluation/esDecorators.ts that actually execute the decorated code to verify semantic equivalence with V8's native implementation where available.
Entry points for modification
- Spec changes to Stage-3 —
src/compiler/transformers/esDecorators.ts. - Class-field/decorator interaction —
src/compiler/transformers/classFields.ts. - Helpers —
src/compiler/factory/emitHelpers.ts(be careful: helpers are also imported viatslibwhen--importHelpersis set, so changes affect the publishedtslibinterface contract). - Type-checking — search
checker.tsforcheckDecorator.
See systems/transformers for the broader pipeline and features/declaration-emit for how decorators interact with .d.ts output.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.