Typescript decorators explained

Decorators offer a powerful way to annotate and modify classes, properties, methods, and accessors in TypeScript. Unlike JavaScript, TypeScript decorators provide an advanced method for meta-programming that is applied at design time rather than at runtime. This article will explain how decorators work in TypeScript, when they are executed, and why they are an important feature for TypeScript developers to understand.

What are TypeScript Decorators?

TypeScript decorators are special kinds of declarations that can be attached to a class declaration, method, accessor, property, or parameter. Decorators use the form @expression, where expression must evaluate to a function that will be called at runtime with information about the decorated declaration.

When are Decorators Executed?

Unlike functions that are called at runtime, decorators are executed when the class file is compiled. This means that the decorator code is run once during the compilation process, not each time an instance of the class is created, allowing decorators to modify the behavior of the class in a consistent manner.

Decorators on Properties, Methods, and Accessors

Decorators are not limited to class declarations alone. They can also be applied to properties, methods, and accessors of a class. When a decorator is applied to these elements, it is called with three specific arguments:

  1. Prototype of the Object: The first argument is the prototype of the class when decorating an instance member. For a static member, this argument is the constructor function of the class.
  2. Key of the Property/Method/Accessor: The second argument is the name of the member being decorated. For a method or accessor, it is the name of the method. For a property, it is the name of the property.
  3. Property Descriptor: The third argument is the property descriptor of the member being decorated. The property descriptor is an object that describes the configuration of the property, method, or accessor, and it includes information such as whether the property is writable or configurable, as well as the get and set accessors for the property.

It’s crucial to note that decorators are applied at the time the class code is evaluated, not when an instance of the class is created. This is a key distinction that means decorators are executed as part of the class definition phase, allowing them to modify the class behavior before any instances are even made.

Code Example:

function log(target: any, propertyName: string, descriptor: PropertyDescriptor) {

const originalMethod = descriptor.value;

descriptor.value = function(...args: any[]) {
console.log(`Calling "${propertyName}" with`, args);
return originalMethod.apply(this, args);
};

}

class Calculator {

@log
add(x: number, y: number) {
return x + y;
}
}

const calculator = new Calculator();

calculator.add(2, 3);
// Console output: Calling "add" with [2, 3]

In this example, the @log decorator is applied to the add method of the Calculator class. The decorator logs a message every time the add method is called, without modifying the method’s original behavior. This example demonstrates how decorators can enhance methods with additional behavior transparently.Understanding the Decorator Composition

The order in which decorators are applied is critical to how they modify the class. When multiple decorators are applied to a single declaration, their evaluation is performed in the reverse order from which they appear. However, their invocation will be as per the order of decorators.

TypeScript vs. JavaScript Decorators

It’s important to note that TypeScript decorators are a language feature and are not the same as decorators in JavaScript, which are still an experimental feature. TypeScript decorators provide more robust and predictable behavior because of their compile-time application.

Conclusion

TypeScript decorators are a potent feature for meta-programming, allowing developers to annotate and modify classes and members. Understanding how and when they are applied can unlock a new level of efficiency and functionality in your TypeScript applications.

,