- Pedro Pini
- Posts
- Decorators in Typescript
Decorators in Typescript
The most used decorator types in Typescript
Some Background
Decorators in TypeScript are a special kind of declaration that can be attached to a class, method, accessor, property, or parameter.
Decorators are essentially functions that can modify the behaviour of the target they are applied. They are often used to add metadata, enable features, or wrap functions with additional logic.
Types of Decorators
1. Class Decorators: Applied to a class.
2. Method Decorators: Applied to methods of a class.
3. Accessor Decorators: Applied to getters or setters.
4. Property Decorators: Applied to properties of a class.
5. Parameter Decorators: Applied to parameters of a class constructor or method.
Why Use Decorators?
1. Code Reusability: Decorators allow for the reuse of common logic across different parts of an application. For example, logging, validation, or authorization can be implemented once and reused across multiple methods or classes.
2. Separation of Concerns: Decorators help in keeping business logic separate from cross-cutting concerns. This makes the codebase cleaner and more maintainable.
3. Enhanced Readability: By using decorators, the intention of a piece of code can be more clearly expressed. For example, an @deprecated
decorator immediately signals that a method should not be used anymore.
4. Metadata Addition: Decorators can be used to add metadata to classes or properties, which can then be used by frameworks or libraries for various purposes, such as dependency injection, serialization, or validation.
5. Aspect-Oriented Programming (AOP): Decorators enable AOP, which allows behaviour to be added to code without modifying the actual code. This is useful for logging, monitoring, or transaction management.
// Class Decorator
// Step 1: Create the decorator
function Logger(target: any) {
console.log(`Class ${target.name} is created`);
}
// Step 2: Apply the decorator to a class
@Logger
class Person {
constructor(public name: string, public age: number) {}
}
// Step 3: Create an instance of the class
const person = new Person('John Doe', 28);
//Method Decorator
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Arguments: ${args}`);
const result = originalMethod.apply(this, args);
console.log(`Result: ${result}`);
return result;
};
return descriptor;
}
class Calculator {
@log
add(a: number, b: number): number {
return a + b;
}
}
const calculator = new Calculator();
calculator.add(4,4);
//Property Decorator
function readOnly(target: any, propertyKey: string) {
Object.defineProperty(target, propertyKey, {
writable: false
});
}
class Person {
@readOnly
name: string;
constructor(name: string) {
this.name = name;
}
}
const person = new Person("Alice");
// Attempt to modify the name property
try {
person.name = "Bob";
} catch (e) {
console.error(e); // Expected error: Cannot assign to read only property 'name'
}
console.log(person.name); // Output: Alice
Reply