TypeScript's decorators provide a way to add meta-programming capabilities to your code. Decorators can be applied to classes, methods, accessors, properties, or parameters, allowing you to modify their behavior. This blog will cover the basics of decorators and explore various types of decorators with examples to help you understand how to use them effectively in your TypeScript projects.
1. Class Decorators
Class decorators are applied to the constructor of a class. They can be used to modify or replace the class definition.
Example:
Suppose you want to add metadata to a class.
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return `Hello, ${this.greeting}`;
}
}In this example, the @sealed decorator seals the Greeter class, preventing any future modifications.
2. Method Decorators
Method decorators are applied to the methods of a class. They can modify the method's behavior or metadata.
Example:
You want to log the execution time of a method.
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
function logExecutionTime(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
const start = performance.now();
const result = originalMethod.apply(this, args);
const end = performance.now();
console.log(`${propertyKey} executed in ${end - start}ms`);
return result;
};
return descriptor;
}
class Calculator {
@logExecutionTime
add(a: number, b: number): number {
return a + b;
}
}
const calculator = new Calculator();
calculator.add(2, 3);Here, the @logExecutionTime decorator logs the execution time of the add method.
3. Accessor Decorators
Accessor decorators are used to observe, modify, or replace an accessor's definition.
Example:
You want to validate the value being set to a property.
function validate(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalSet = descriptor.set;
descriptor.set = function (value: number) {
if (value < 0) {
throw new Error('Value must be non-negative');
}
originalSet.call(this, value);
};
return descriptor;
}
class Account {
private _balance: number = 0;
@validate
set balance(amount: number) {
this._balance = amount;
}
get balance() {
return this._balance;
}
}
const account = new Account();
account.balance = 100; // Works fine
// account.balance = -50; // Throws error The @validate decorator ensures that the balance is non-negative.
4. Property Decorators
Property decorators are used to add metadata to properties.
Example:
You want to mark a property as read-only.
function readonly(target: any, propertyKey: string) {
Object.defineProperty(target, propertyKey, {
writable: false
});
}
class Employee {
@readonly
id: number = 12345;
}
const emp = new Employee();
// emp.id = 54321; // Error: Cannot assign to 'id' because it is a read-only property.In this example, the @readonly decorator makes the id property immutable.
5. Parameter Decorators
Parameter decorators are used to add metadata to method parameters.
Example:
You want to inject a service into a method parameter.
function inject(serviceIdentifier: string) {
return function (target: any, propertyKey: string | symbol, parameterIndex: number) {
const existingInjectedParameters = Reflect.getOwnMetadata('injectedParameters', target, propertyKey) || [];
existingInjectedParameters.push({ serviceIdentifier, parameterIndex });
Reflect.defineMetadata('injectedParameters', existingInjectedParameters, target, propertyKey);
};
}
class Service {}
class Consumer {
constructor(@inject('service') private service: Service) {}
}The @inject decorator adds metadata to the constructor parameter, allowing dependency injection.
Decorators in TypeScript provide powerful capabilities for meta-programming, enabling you to write cleaner and more maintainable code. By understanding and utilizing decorators, you can enhance your TypeScript projects with advanced functionalities and custom behaviors. Start exploring decorators today to see how they can transform your development workflow!
Learn more about decorators in the TypeScript Handbook.