iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Decorators Reference

Reference for stage-3 decorator signatures in TypeScript 5+. Each decorator kind has its own context type — sticking to the right shape keeps the compiler happy.

Class decorator

TS
type ClassDecorator<TFunction extends abstract new (...args: any) => any> = (
    value:   TFunction,
    context: ClassDecoratorContext<TFunction>,
) => TFunction | void;

Method decorator

TS
type MethodDecorator<This, Args extends any[], Return> = (
    value:   (this: This, ...args: Args) => Return,
    context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>,
) => ((this: This, ...args: Args) => Return) | void;

Field decorator

TS
type FieldDecorator<This, Value> = (
    value:   undefined,
    context: ClassFieldDecoratorContext<This, Value>,
) => ((this: This, value: Value) => Value) | void;

Getter / setter / accessor

Decorator typeContext type
getterClassGetterDecoratorContext<This, Value>
setterClassSetterDecoratorContext<This, Value>
auto-accessorClassAccessorDecoratorContext<This, Value>

Context properties (all kinds)

PropertyMeans
kind"class" / "method" / "field" / "getter" / …
nameMember name (string or symbol).
staticBoolean — is the member static?
privateBoolean — # private?
addInitializer(fn)Schedule code to run when the decorated thing is created.
metadataObject for sharing data between decorators (4.1+).
access{ has, get, set } — type-safe member access.

Decorator factories

TS
function memoize<TArgs extends unknown[], TRet>(opts: { max: number } = { max: 100 }) {
    return function (
        target:  (...args: TArgs) => TRet,
        _ctx:    ClassMethodDecoratorContext,
    ) {
        const cache = new Map<string, TRet>();
        return function (this: unknown, ...args: TArgs) {
            const key = JSON.stringify(args);
            if (!cache.has(key)) {
                if (cache.size >= opts.max) cache.delete(cache.keys().next().value as string);
                cache.set(key, target.apply(this, args));
            }
            return cache.get(key)!;
        };
    };
}

class Service {
    @memoize({ max: 50 })
    expensive(x: number) { /* ... */ return x * x; }
}

Experimental (legacy) decorators

Pre-stage-3 decorators are still around — enable with "experimentalDecorators": true. Common in Angular and older NestJS. Different signatures; not interchangeable with the new ones.

Tip: For new code, prefer stage-3 decorators. They match the future JS spec, integrate with type-aware tooling, and have less weird metadata machinery.

Example

Example
// TS 5 implements Stage-3 decorators.
// Signature shapes:
//   ClassDecoratorContext
//   ClassMethodDecoratorContext
//   ClassFieldDecoratorContext
//   ClassGetterDecoratorContext / Setter / Accessor
//
// Legacy decorators still work behind --experimentalDecorators.
console.log('Decorators are functions that wrap class members');
Try it Yourself »

Exercise

Method-decorator context type name.

ctx: <This, Fn>

Test yourself

Q1. A class decorator's context type is…
Q2. "addInitializer" is for…
Q3. Stage 3 decorators landed in TS…

Discussion

Loading…