How to Create Custom TypeScript Decorators
Decorators are a feature in TypeScript that allows for modifying classes, methods, properties, or parameters at runtime. They are special functions that provide meta-programming capabilities. In TypeScript, decorators are often used in frameworks like Angular to enhance functionality. This article explains how to create custom decorators step-by-step.
Types of Decorators in TypeScript
There are four main types of decorators in TypeScript:
- Class Decorators
- Method Decorators
- Accessor Decorators
- Property Decorators
Enabling Decorators in TypeScript
To use decorators in a TypeScript project, the experimentalDecorators
option must be enabled in the tsconfig.json
file.
{
"compilerOptions": {
"experimentalDecorators": true
}
}
Creating a Class Decorator
Class decorators are applied to the constructor of a class. They are useful for adding metadata or functionality to the class. Here's an example of how to create a simple class decorator.
function logClass(constructor: Function) {
console.log(`Class ${constructor.name} is created`);
}
@logClass
class Person {
constructor(public name: string) {}
}
const person = new Person("John");
// Output: Class Person is created
Creating a Method Decorator
Method decorators are applied to class methods. They can be used to modify or observe the behavior of the method. Below is an example of a method decorator that logs the method execution.
function logMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Method ${propertyKey} is called with arguments:`, args);
return originalMethod.apply(this, args);
};
return descriptor;
}
class Calculator {
@logMethod
add(a: number, b: number) {
return a + b;
}
}
const calc = new Calculator();
calc.add(2, 3);
// Output: Method add is called with arguments: [2, 3]
Creating a Property Decorator
Property decorators can be used to observe or modify properties. Here's an example where a property decorator ensures that a property has a default value.
function defaultValue(value: any) {
return function (target: any, propertyKey: string) {
let propertyValue = value;
const getter = function () {
return propertyValue;
};
const setter = function (newValue: any) {
propertyValue = newValue || value;
};
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true,
});
};
}
class User {
@defaultValue('Anonymous')
name!: string;
}
const user = new User();
console.log(user.name); // Output: Anonymous
user.name = 'Alice';
console.log(user.name); // Output: Alice
Creating a Parameter Decorator
Parameter decorators are applied to a method's parameters. They can be useful for tasks like validation or logging arguments. Here's an example of a parameter decorator.
function logParameter(target: any, propertyKey: string, parameterIndex: number) {
console.log(`Parameter at index ${parameterIndex} in method ${propertyKey} is being decorated`);
}
class Vehicle {
drive(@logParameter speed: number) {
console.log(`Driving at speed ${speed}`);
}
}
const vehicle = new Vehicle();
vehicle.drive(50);
// Output: Parameter at index 0 in method drive is being decorated
Conclusion
Decorators in TypeScript offer powerful meta-programming capabilities that can enhance and extend the functionality of classes, methods, and properties. By using custom decorators, it's possible to create reusable, efficient, and organized code structures. This guide demonstrated the creation of different types of decorators: class, method, property, and parameter.