What are decorators in TypeScript?

Key takeaways

  • Decorators are a feature in TypeScript that allows developers to modify the behavior of classes, methods, properties, or parameters.

  • Decorators are defined using the @ symbol followed by the decorator name.

  • The benefits of using decorators are following:

    • Code reusability: Encapsulate functionalities like logging and validation.

    • Declarative syntax: Enhance readability and maintainability.

    • Separation of concerns: Clearly distinguish business logic from additional functionalities.

  • Limitations to using decorators may be browser support, excessive complexity, or performance overhead.

Decorators are a powerful feature in TypeScript that enables developers to modify the behavior of classes, methods, properties, or parameters in a declarative way. They provide a way to add metadata to code, making it possible to enhance or change the functionality of existing code without modifying it directly. Decorators are a form of metaprogramming that allows us to write reusable code such as logging, validation, dependency injection, etc.

For instance, consider a web application that needs to log user activities and enforce access control. Using decorators, we can automatically log every method call in a class and check user roles before executing sensitive methods.

Syntax for decorators

Decorators are attached to entities (classes, functions, and more) with the following syntax:

@nameOfOurDecorator

The @ symbol is part of the syntax and denotes that the following identifier is a decorator. As the name suggests, after that, we specify the name of our decorator, through which we customize the entity's functionality.

Benefits of decorators

  1. Code reusability: Decorators encapsulate common functionalities like logging, validation, and authentication, promoting code reuse across different application parts and reducing duplication.

  2. Declarative syntax: Decorators clean and declarative syntax makes applying metadata and modifying behavior easier, enhancing code readability and maintainability.

  3. Separation of concerns: By separating concerns into individual decorators, developers can clearly distinguish between core business logic, making it easier to manage.

Limitations of decorators

  1. Browser support: Decorators may not be fully supported in all JavaScript environments, potentially causing compatibility issues, especially in older browsers.

  2. Complexity: Decorators, particularly when nested or used extensively, can introduce complexity to the codebase, making it harder to understand and maintain.

  3. Performance overhead: Applying decorators at runtime can incur a performance overhead, especially in scenarios with complex decorator logic, potentially impacting the application's performance.

Coding example for decorators

If we're running our project locally, we need to edit our tsconfig.json file to something like the following:

{
"compilerOptions": {
...
"experimentalDecorators": true, // This line to enable decorators
...
}
}

Let's create a calculator. In the given code, decorators are used to log each method call in the Calculator class. They automatically record the method name and parameters and return values, providing a clear audit trail for operations like addition, subtraction, multiplication, and division.

function logMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const prev = descriptor.value;

  descriptor.value = function (...args: any[]) {
    console.log(`The function called is ${propertyKey} with parameters: ${JSON.stringify(args)}`);
    const res = prev.apply(this, args);
    console.log(`The function ${propertyKey} returned: ${res}`);
    return res;
  };

  return descriptor;
}

class Calculator {
  @logMethod
  add(num1: number, num2: number) {
    return num1 + num2;
  }

  @logMethod
  sub(num1: number, num2: number) {
    return num1 - num2;
  }

  @logMethod
  mul(num1: number, num2: number) {
    return num1 * num2;
  }

  @logMethod
  div(a: number, b: number) {
    if (b === 0) {
      throw new Error("Cannot divide by zero");
    }
    return a / b;
  }
}

const calculator = new Calculator();
console.log(calculator.add(23, 8));
console.log(calculator.sub(6, 2));
console.log(calculator.mul(9, 7));
console.log(calculator.div(30, 10));
A simple calculator using decorators

Code explanation

The code above can be explained as follows:

  • Line 1: Defines the logMethod decorator function.

  • Line 4: Replaces the original method with a modified version that adds logging.

  • Lines 5–7: Logs the method name, parameters, and return value when the method is called.

  • Lines 16–18: The add method in the Calculator class with the decorator applied.

  • Lines 21–23: The sub method in the Calculator class with the decorator applied.

  • Lines 26–28: The mul method in the Calculator class with the decorator applied.

  • Lines 31–36: The div method in the Calculator class with the decorator applied, including error handling for division by zero.

  • Lines 39–43: Calls the methods in the Calculator class and logs the results.

Note: In the above code example, each method in the Calculator class is appended with the @logMethod decorator. This logs the method call with the arguments passed before executing and logging the return value after execution.

Quiz

Lets assess your understanding of decorators by choosing the correct answer in the quiz below:

1

In the provided example, what is the role of the logMethod function?

A)

To perform arithmetic calculations.

B)

To validate user inputs.

C)

To log method calls and return values.

D)

To handle exceptions during method execution.

Question 1 of 20 attempted

Conclusion

Decorators in TypeScript provide a clean and reusable way to enhance code functionality without directly altering it. By attaching them to methods, we can automatically log method calls, validate inputs, or perform other tasks.

Free Resources

Copyright ©2025 Educative, Inc. All rights reserved