TypeScript Decorators
Add metadata and modify class behavior
✨ What are Decorators?
Decorators are special functions that modify classes, methods, properties, or parameters. They use the @ symbol and provide a way to add annotations and meta-programming syntax. Enable them in tsconfig.json with "experimentalDecorators": true.
// Enable in tsconfig.json: "experimentalDecorators": true
@sealed
class Person {
@readonly
name: string = "John";
}
Types of Decorators
Class Decorators
Modify or observe classes
@Component
class MyComponent {
// class code
}
Method Decorators
Modify method behavior
class API {
@log
getData() {
// method code
}
}
Property Decorators
Add metadata to properties
class User {
@required
email: string;
}
Parameter Decorators
Annotate method parameters
method(@required id: number) {
// method code
}
🔹 Class Decorator
Class decorators are applied to the class constructor:
// Decorator function
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
console.log(`${constructor.name} is now sealed`);
}
@sealed
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
}
const person = new Person("Alice");
console.log(person.name); // Alice
// Try to add new property (won't work in strict mode)
// Person.prototype.age = 30; // Error in strict mode
Output:
Person is now sealed Alice
🔹 Method Decorator
Method decorators can modify or observe method behavior:
// Log decorator
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling ${propertyKey} with args: ${JSON.stringify(args)}`);
const result = originalMethod.apply(this, args);
console.log(`Result: ${result}`);
return result;
};
return descriptor;
}
class Calculator {
@log
add(a: number, b: number): number {
return a + b;
}
@log
multiply(a: number, b: number): number {
return a * b;
}
}
const calc = new Calculator();
calc.add(5, 3);
calc.multiply(4, 2);
Output:
Calling add with args: [5,3] Result: 8 Calling multiply with args: [4,2] Result: 8
🔹 Property Decorator
Property decorators add metadata to class properties:
// Readonly decorator
function readonly(target: any, propertyKey: string) {
Object.defineProperty(target, propertyKey, {
writable: false,
configurable: false
});
console.log(`${propertyKey} is now readonly`);
}
class Config {
@readonly
apiUrl: string = "https://api.example.com";
@readonly
version: string = "1.0.0";
}
const config = new Config();
console.log(config.apiUrl); // https://api.example.com
console.log(config.version); // 1.0.0
// Try to modify (won't work)
// config.apiUrl = "new-url"; // Error in strict mode
Output:
apiUrl is now readonly version is now readonly https://api.example.com 1.0.0
🔹 Decorator Factory
Decorator factories allow you to customize decorator behavior:
// Decorator factory with parameters
function minLength(length: number) {
return function(target: any, propertyKey: string) {
let value: string;
const getter = () => value;
const setter = (newVal: string) => {
if (newVal.length < length) {
console.log(`${propertyKey} must be at least ${length} characters`);
} else {
value = newVal;
}
};
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter
});
};
}
class User {
@minLength(3)
username: string = "";
@minLength(8)
password: string = "";
}
const user = new User();
user.username = "Jo"; // username must be at least 3 characters
user.username = "John"; // ✅ Valid
user.password = "pass"; // password must be at least 8 characters
user.password = "password123"; // ✅ Valid
Output:
username must be at least 3 characters password must be at least 8 characters
🔹 Multiple Decorators
You can apply multiple decorators to the same target:
function first() {
console.log("first(): factory evaluated");
return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("first(): called");
};
}
function second() {
console.log("second(): factory evaluated");
return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("second(): called");
};
}
class Example {
@first()
@second()
method() {
console.log("method called");
}
}
const example = new Example();
example.method();
Output:
first(): factory evaluated second(): factory evaluated second(): called first(): called method called
Note: Decorators are evaluated top-to-bottom, but executed bottom-to-top.
💡 Key Points:
-
Enable decorators with
"experimentalDecorators": truein tsconfig.json -
Decorators use the
@symbol before the target - Class decorators modify class constructors
- Method decorators can wrap or modify methods
- Property decorators add metadata to properties
- Decorator factories allow parameterized decorators
- Multiple decorators execute bottom-to-top