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

or to participate.