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 thetsconfig.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.
- The prototype of the class
- The name of the method
- 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.
- The prototype of the class
- The name of the method
- 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.