TypeScript decorators are a powerful feature that enables developers to add metadata and modify the behavior of classes, methods, properties, or parameters. They are essentially functions that are prefixed with the @
symbol and are used to annotate and modify the elements they are applied to. In this article, we will explore three diverse examples of TypeScript decorators, providing practical use cases that can enhance your coding practices.
Imagine you want to track the instantiation of classes for debugging purposes or performance monitoring. A class decorator can help you log each time a class is created.
function LogClass(target: Function) {
const original = target;
const newConstructor: any = function (...args: any[]) {
console.log(`Creating instance of: ${original.name}`);
return new original(...args);
};
newConstructor.prototype = original.prototype;
return newConstructor;
}
@LogClass
class User {
constructor(public name: string) {}
}
const user1 = new User('Alice'); // Logs: Creating instance of: User
A method decorator can be used to measure the execution time of specific methods, which is particularly useful for performance optimization or tracking the efficiency of your code.
function LogExecutionTime(target: any, propertyName: 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(`Execution time of ${propertyName}: ${end - start} ms`);
return result;
};
}
class Calculator {
@LogExecutionTime
add(a: number, b: number) {
return a + b;
}
}
const calc = new Calculator();
calc.add(5, 10); // Logs execution time of add method
Property decorators can be used to enforce validation rules. For instance, you might want to ensure that a property is a string and not empty in a class.
function IsString(target: any, propertyName: string) {
let value: string;
const getter = () => value;
const setter = (newValue: string) => {
if (typeof newValue !== 'string' || newValue.trim() === '') {
throw new Error(`Invalid value for ${propertyName}. It must be a non-empty string.`);
}
value = newValue;
};
Object.defineProperty(target, propertyName, { get: getter, set: setter });
}
class Product {
@IsString
name: string;
}
const product = new Product();
product.name = 'Laptop'; // Works
product.name = ''; // Throws: Invalid value for name. It must be a non-empty string.
By implementing these examples of TypeScript decorators, you can streamline your code, improve performance, and enforce better practices in your development process.