In this article, we'll take a dive into decorators in TypeScript, explore their types, and provide practical examples to help you harness their full potential.
What Are Decorators? 🎨
At their core, decorators are simply functions that are applied to classes, methods, properties, or parameters using the @
symbol. They can be used for various purposes, including:
- Logging and debugging: Adding logging statements before and after method execution.
- Validation: Checking the validity of inputs or outputs.
- Authorization: Controlling access to certain methods or routes.
- Memoization: Caching the results of expensive operations.
- Dependency Injection: Automatically injecting dependencies into classes.
- Metadata Collection: Storing additional information about classes or properties.
Class Decorators 🏛️
Class decorators are applied to classes. They receive the class constructor as their only parameter and can be used to modify the class or add metadata to it.
function MyDecorator(target: Function) {
console.log('Class decorator called on', target);
}
@MyDecorator
class MyClass {
constructor() {
console.log('MyClass constructor');
}
}
// Output:
// Class decorator called on [Function: MyClass]
// MyClass constructor
Method Decorators 🛠️
Method decorators are applied to methods within a class. They receive three parameters: the target class prototype, the method name, and a property descriptor.
function LogMethod(target: Object, key: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling ${key} with arguments: ${args}`);
const result = originalMethod.apply(this, args);
console.log(`${key} returned: ${result}`);
return result;
};
return descriptor;
}
class Calculator {
@LogMethod
add(a: number, b: number): number {
return a + b;
}
}
const calc = new Calculator();
calc.add(2, 3);
// Output:
// Calling add with arguments: 2,3
// add returned: 5
Property Decorators 🏠
Property decorators are used to modify properties within a class. They receive two parameters: the target class prototype and the property name.
function Validate(target: any, key: string) {
let value = target[key];
const getter = () => value;
const setter = (newValue: any) => {
if (typeof newValue !== 'number' || newValue < 0) {
throw new Error('Invalid value');
}
value = newValue;
};
Object.defineProperty(target, key, {
get: getter,
set: setter,
});
}
class Product {
@Validate
price: number;
constructor(price: number) {
this.price = price;
}
}
const product = new Product(50);
product.price = -10; // Throws an error
// Output:
// Error: Invalid value
Parameter Decorators 🎯
Parameter decorators are applied to parameters of methods within a class. They receive three parameters: the target class prototype, the method name, and the index of the parameter.
const Injectable = (): ParameterDecorator => {
return (target: any, key: string | symbol, index: number) => {
// Register the dependency injection here
console.log(`Injecting parameter at index ${index} in method ${key}`);
};
};
class UserService {
constructor(private readonly logger: Logger) {}
@Injectable()
log(message: string) {
this.logger.log(message);
}
}
const logger = new Logger();
const userService = new UserService(logger);
userService.log('Hello, Decorators!');
// Output:
// Injecting parameter at index 0 in method log
// Hello, Decorators!
Top comments (0)