Decorators
Concept
Decorators are a syntactic construct used to modify the behavior of a class at definition time. They have the following characteristics:
- Start with the
@symbol, followed by an expression. - The expression after
@must be a function (or evaluate to a function). - The function receives parameters related to the decorated object.
- The function either returns nothing or returns a new object to replace the decorated target.
function simpleDecorator(target: any, context: any) {
console.log('hi, this is ' + target);
return target;
}
@simpleDecorator
class A {} // "hi, this is class A {}"
// Valid decorators
@myFunc
@myFuncFactory(arg1, arg2)
@libraryModule.prop
@someObj.method(123)
@(wrap(dict['prop']))
@frozen
class Foo {
@configurable(false)
@enumerable(true)
method() {}
@throttle(500)
expensiveMethod() {}
}
Decorator Structure
type Decorator = (
value: DecoratedValue, // The decorated object
context: {
// Context object, described by TypeScript’s native ClassMethodDecoratorContext interface
kind: string; // Type of the decorated object (e.g., class, method, field)
name: string | symbol; // Name of the decorated object (e.g., class name, property name)
addInitializer?(initializer: () => void): void; // Function to add class initialization logic
static?: boolean; // Whether the decorated object is a static class member
private?: boolean; // Whether the decorated object is a private class member
access: {
// Object containing get and set methods for the value
get?(): unknown;
set?(value: unknown): void;
};
}
) => void | ReplacementValue;
Class Decorators
function Greeter(value: any, context: any) {
if (context.kind === 'class') {
value.prototype.greet = function () {
console.log('Hello');
};
}
}
@Greeter
class User {}
let u = new User();
u.greet(); // "Hello"
Method Decorators
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
@log
greet() {
console.log(`Hello, my name is ${this.name}.`);
}
}
function log(originalMethod: any, context: ClassMethodDecoratorContext) {
const methodName = String(context.name);
function replacementMethod(this: any, ...args: any[]) {
console.log(`LOG: Entering method '${methodName}'.`);
const result = originalMethod.call(this, ...args);
console.log(`LOG: Exiting method '${methodName}'.`);
return result;
}
return replacementMethod;
}
const person = new Person('John');
person.greet();
// "LOG: Entering method 'greet'."
// "Hello, my name is John."
// "LOG: Exiting method 'greet'."
Property Decorators
function logged(value: any, context: any) {
const { kind, name } = context;
if (kind === 'field') {
return function (initialValue: any) {
console.log(`initializing ${name} with value ${initialValue}`);
return initialValue;
};
}
}
class Color {
@logged name = 'green';
}
const color = new Color();
// "initializing name with value green"
Getter and Setter Decorators
class C {
@lazy
get value() {
console.log('Calculating...');
return 'Expensive computation result';
}
}
function lazy(value: any, { kind, name }: any) {
if (kind === 'getter') {
return function (this: any) {
const result = value.call(this);
Object.defineProperty(this, name, {
value: result,
writable: false,
});
return result;
};
}
return;
}
const inst = new C();
inst.value;
// "Calculating..."
// "Expensive computation result"
inst.value;
// "Expensive computation result"
Accessor Decorators
type ClassAutoAccessorDecorator = (
value: {
get: () => unknown;
set(value: unknown): void;
},
context: {
kind: 'accessor';
name: string | symbol;
access: { get(): unknown; set(value: unknown): void };
static: boolean;
private: boolean;
addInitializer(initializer: () => void): void;
}
) => {
get?: () => unknown;
set?: (value: unknown) => void;
init?: (initialValue: unknown) => unknown;
} | void;
Decorator Application Order: Method decorators and property decorators are applied first, followed by class decorators.



