TypeScript Decorators & Metadata
Decorators provide a way to add annotations and meta-programming syntax for class declarations and members. They are functions that modify classes and their members.
Class Decorators
// Simple class decorator
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class Person {
constructor(public name: string) {}
}
// Decorator factory (configurable)
function logger(prefix: string) {
return function(constructor: Function) {
console.log(`${prefix}: ${constructor.name}`);
};
}
@logger('Creating')
class User {
constructor(public name: string) {}
}
// Decorator that replaces constructor
function timestamp<T extends { new(...args: any[]): {} }>(constructor: T) {
return class extends constructor {
timestamp = new Date();
};
}
@timestamp
class Document {
constructor(public title: string) {}
}
const doc = new Document('My Doc');
console.log((doc as any).timestamp); // Date objectMethod Decorators
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling ${propertyKey} with`, args);
const result = original.apply(this, args);
console.log(`Result:`, result);
return result;
};
return descriptor;
}
class Calculator {
@log
add(a: number, b: number): number {
return a + b;
}
}
const calc = new Calculator();
calc.add(2, 3); // Logs: Calling add with [2, 3], Result: 5
// Performance measurement
function measure(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = async function(...args: any[]) {
const start = performance.now();
const result = await original.apply(this, args);
const end = performance.now();
console.log(`${propertyKey} took ${(end - start).toFixed(2)}ms`);
return result;
};
return descriptor;
}
class DataProcessor {
@measure
async processData(data: any[]): Promise<any[]> {
// Simulate processing
await new Promise(resolve => setTimeout(resolve, 100));
return data.map(x => x * 2);
}
}Property & Parameter Decorators
// Property decorator
function required(target: any, propertyKey: string) {
let value: any;
const getter = () => value;
const setter = (newValue: any) => {
if (!newValue) {
throw new Error(`${propertyKey} is required`);
}
value = newValue;
};
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
}
class User {
@required
email!: string;
name?: string;
}
const user = new User();
// user.email = ''; // Error: email is required
user.email = 'test@example.com'; // ✅
// Parameter decorator
function logParameter(target: any, propertyKey: string, parameterIndex: number) {
console.log(`Parameter decorator: ${propertyKey}[${parameterIndex}]`);
}
class Greeter {
greet(@logParameter message: string): void {
console.log(message);
}
}Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free