Decorators in TypeScript

Decorators in TypeScript

This article explains decorators in TypeScript.

YouTube Video

Decorators in TypeScript

TypeScript decorators are a mechanism for adding additional functionality or behavior to classes, methods, accessors, properties, or parameters. Decorators are a powerful tool for enhancing code readability and reusability.

Basics of Decorators

A decorator is a function that injects additional functionality into a class or class members. Decorators were introduced in TypeScript 1.5 and have also been proposed for the ECMAScript standard.

1{
2  "compilerOptions": {
3    "experimentalDecorators": true
4  }
5}
  • To use decorators, you need to enable the experimentalDecorators option in the tsconfig.json file.

Types of Decorators

In TypeScript, you can use the following five decorators.

  • Class Decorator A decorator applied to a class.
  • Method Decorator A decorator applied to a method of a class.
  • Accessor Decorator A decorator applied to a getter or setter of a class property.
  • Property Decorator A decorator applied to a property of a class.
  • Parameter Decorator A decorator applied to a parameter of a method.

Class Decorator

Class decorators are decorators applied to classes. Class decorators are written just above the class definition and can access the class constructor. They are mainly used to change class behavior or add metadata.

 1function Logger(constructor: Function) {
 2    console.log(`Class ${constructor.name} is being constructed`);
 3}
 4
 5@Logger
 6class Person {
 7    constructor(public name: string) {}
 8}
 9
10const person = new Person('John');
11// Output: Class Person is being constructed

In this example, the Logger decorator is applied to the class, and a message is displayed in the console when the class is initialized.

Method Decorator

Method decorators are applied to class methods and can alter method calls and behavior. A method decorator takes three arguments.

  1. The prototype of the class
  2. The name of the method
  3. The property descriptor of the method
 1function LogExecutionTime(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
 2    const originalMethod = descriptor.value;
 3
 4    descriptor.value = function (...args: any[]) {
 5        console.time(propertyKey);
 6        const result = originalMethod.apply(this, args);
 7        console.timeEnd(propertyKey);
 8        return result;
 9    };
10}
11
12class MathOperations {
13  @LogExecutionTime
14  add(a: number, b: number): number {
15    return a + b;
16  }
17}
18
19const math = new MathOperations();
20math.add(2, 3);
21// Output: add: 0.000ms (execution time is displayed)

In this example, the LogExecutionTime decorator is applied to the method, and the execution time of the method is logged.

Accessor Decorator

Accessor decorators are applied to getter or setter of class properties. They are useful for adding behavior when changing property values.

 1function LogAccess(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
 2    const originalGet = descriptor.get;
 3    const originalSet = descriptor.set;
 4
 5    if (originalGet) {
 6        descriptor.get = function () {
 7            console.log(`Getter called for ${propertyKey}`);
 8            return originalGet.call(this);
 9        };
10    }
11
12    if (originalSet) {
13        descriptor.set = function (value: any) {
14            console.log(`Setter called for ${propertyKey} with value: ${value}`);
15            originalSet.call(this, value);
16        };
17    }
18}
19
20class Car {
21    private _speed: number = 0;
22
23    @LogAccess
24    get speed() {
25        return this._speed;
26    }
27
28    set speed(value: number) {
29        this._speed = value;
30    }
31}
32
33const car = new Car();
34car.speed = 120;               // Setter called for speed with value: 120
35console.log(car.speed);       // Getter called for speed → 120

In this example, an accessor decorator is used to log output when the getter and setter are called.

Property Decorator

Property decorators are applied to class properties but cannot directly change the property's value or behavior. They are used to obtain metadata of properties.

 1function Readonly(target: any, propertyKey: string) {
 2    Object.defineProperty(target, propertyKey, {
 3        writable: false
 4    });
 5}
 6
 7class Book {
 8    @Readonly
 9    title: string = "TypeScript Guide";
10}
11
12const book = new Book();
13book.title = "New Title"; // Error: Cannot assign to read only property 'title'

In this example, the Readonly decorator is applied to the title property, and the property is made read-only.

Parameter Decorator

Parameter decorators are applied to the parameters of a method. They are usually used to store metadata or validate parameters. Decorators take three arguments.

  1. The prototype of the class
  2. The name of the method
  3. Parameter Index
 1function LogParameter(target: any, propertyKey: string, parameterIndex: number) {
 2    console.log(`Parameter at index ${parameterIndex} in method ${propertyKey} was decorated.`);
 3}
 4
 5class User {
 6    greet(@LogParameter message: string) {
 7        console.log(message);
 8    }
 9}
10
11const user = new User();
12user.greet('Hello!');
13// Output: Parameter at index 0 in method greet was decorated.

In this example, the LogParameter decorator is applied to the first parameter of the greet method, and when the method is called, it logs that the parameter is decorated.

Practical Examples of Decorators

Decorators are widely used in frameworks like Angular, especially for dependency injection and metadata definition. For example, use the @Component decorator to define Angular components like below.

1@Component({
2    selector: 'app-root',
3    template: '<h1>Hello World</h1>',
4})
5export class AppComponent {}

Thus, decorators are often used as core parts of frameworks and libraries, helping keep code concise and clear.

Summary of Decorators

TypeScript decorators are powerful tools that flexibly add functionality to classes, methods, and properties. Using custom decorators improves code maintainability and reusability, enabling further abstraction. Decorators play an important role in frameworks like Angular and NestJS, and understanding them helps in deeply understanding how these frameworks work.

You can follow along with the above article using Visual Studio Code on our YouTube channel. Please also check out the YouTube channel.

YouTube Video