TypeScript decorators are functions that enable developers to declare and apply metadata or modify the behavior of classes, methods, properties, or parameters in a declarative manner. Decorators are prefixed with the @ symbol and are commonly used in frameworks like Angular for features such as dependency injection and component configuration.
Decorators empower developers to extend and customize the behavior of their code in a modular and reusable fashion, enhancing the expressiveness and maintainability of TypeScript applications. It can be applied to a wide range of targets, providing a flexible mechanism for creating sophisticated and extensible architectures.
// Class decorator that logs when a class is instantiated
function logClass(target: any) {
console.log(`Class ${target.name} is instantiated.`);
}
@logClass
class MyClass {
// Class implementation
}
// Output: Class MyClass is instantiated.
const instance = new MyClass();
// Method decorator that logs when a method is called
function logMethod(target: any, key: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Method ${key} is called with arguments: ${args}`);
return originalMethod.apply(this, args);
};
}
class ExampleClass {
@logMethod
myMethod() {
// Method implementation
}
}
const instance = new ExampleClass();
instance.myMethod();
// Output: Method myMethod is called with arguments: []
Property Decorator
// Property decorator that adds a prefix to a property value
function addPrefix(prefix: string) {
return function (target: any, key: string) {
let value = target[key];
const getter = function () {
return prefix + value;
};
const setter = function (newVal: string) {
value = newVal;
};
Object.defineProperty(target, key, {
get: getter,
set: setter,
enumerable: true,
configurable: true,
});
};
}
class ExampleClass {
@addPrefix("Mr. ")
name: string = "John";
}
const instance = new ExampleClass();
console.log(instance.name); // Output: Mr. John
Parameter Decorator
// Parameter decorator that logs the value of a method parameter
function logParameter(target: any, key: string, index: number) {
console.log(`Parameter value: ${key} at index ${index}`);
}
class ExampleClass {
myMethod(@logParameter param1: string, param2: number) {
// Method implementation
}
}
const instance = new ExampleClass();
instance.myMethod("Hello", 42);
// Output: Parameter value: myMethod at index 0
Decorator Composition
// Decorator that composes multiple decorators
function composeDecorators(...decorators: Function[]): Function {
return function (target: any) {
decorators.forEach((decorator) => decorator(target));
};
}
function logClass(target: any) {
console.log(`Class ${target.name} is instantiated.`);
}
function addPrefix(prefix: string) {
return function (target: any, key: string) {
// Property decorator logic
};
}
@composeDecorators(logClass, addPrefix("Mr. "))
class DecoratedClass {
// Class implementation
}
const instance = new DecoratedClass();
// Output: Class DecoratedClass is instantiated.
Advantages of using Decorators
- Modular and reusable code: Add behavior without modifying existing code.
- Improved code organization: Separate concerns and enhance readability.
- Metaprogramming capabilities: Add dynamic behavior based on metadata.
Conclusion
TypeScript decorators are functions that allow developers to declaratively attach metadata or modify the behavior of classes, methods, properties, and parameters. Employing the @decorator syntax, decorators enhance code modularity and reusability, commonly finding application in frameworks to streamline features like dependency injection and configuration in a clean, declarative fashion.