A Decorator represents a unique type of declaration that can be utilized on classes, methods, accessors, properties, or parameters. Essentially, decorators are functions that begin with the @expression symbol, where the expression must resolve to a function that will be executed during runtime, providing details about the decorated declaration.
Note: Decorators are an experimental feature proposed for ES7. It is already in use by some of the JavaScript frameworks including Angular 2. The Decorators may change in future releases.
To activate experimental support for decorators, it is necessary to set the experimentalDecorators compiler option, either via the command line or within our tsconfig.json file:
Command Line
$tsc --target ES5 --experimentalDecorators
tsconfig.json
{
"compilerOptions": {
"target": "ES5",
"experimentalDecorators": true
}
}
Purpose
TypeScript Decorators are utilized to incorporate annotations and metadata into existing code in a declarative manner.
Decorator Factories
To tailor how a decorator is applied to a declaration, one can create a decorator factory. A decorator factory is a function that produces the expression which the decorator will execute during runtime.
A decorator factory can be implemented as follows:
function color(value: string) { // this is the decorator factory
return function (target) { // this is the decorator
// do something with 'target' and 'value'...
}
}
Decorator Composition
Multiple decorators can be applied to a single declaration. The subsequent examples illustrate this concept effectively.
On a single line
@f @g x
On multiple lines
@f
@g
x
Types of Decorators
TypeScript incorporates these categories of Decorators:
- Class Decorators
- Method Decorators
- Accessor Decorators
- Property Decorators
- Parameter Decorators
1. Class Decorators
A class decorator is specified immediately prior to the class definition and indicates the behaviors of that class. It is applied to the class's constructor. Class decorators can be utilized to monitor, alter, or substitute a class definition. If a class decorator yields a value, it will supplant the class declaration with the provided constructor function.
Example:
@sealed
class Person {
msg: string;
constructor(message: string) {
this.msg = message;
}
show() {
return "Hello, " + this.msg;
}
}
In the aforementioned example, the execution of the @sealed decorator will seal both the constructor and its prototype, thereby preventing any inheritance from the Person class.
2. Method Decorators
A Method Decorator is specified immediately prior to a method declaration. It operates on the property descriptor of that method. This decorator can be utilized to monitor, alter, or substitute a method's definition. Method decorators cannot be employed within a declaration file.
The expression for the method decorator function accepts three arguments. They are:
- Either the constructor function of the class for a static member or the prototype of the class for an instance member.
- The member name.
- The Property Descriptor for the member.
Example:
In the following example, the @log decorator is utilized to record the addition of a new item.
class Item {
itemArr: Array;
constructor() {
this.itemArr = [];
}
@log
Add(item: string): void {
this.itemArr.push(item);
}
GetAll(): Array {
return this.itemArr;
}
}
3. Accessor Decorators
An Accessor Decorator is specified immediately prior to an accessor declaration. It is utilized on the property descriptor associated with the accessor. This decorator can be employed to monitor, alter, or substitute the definitions of an accessor.
Note: An accessor is a getter and setter property of the class declaration.
The expression for the accessor decorator function accepts three arguments. They are:
- Either the constructor function of the class for a static member or the prototype of the class for an instance member.
- The member name.
- The Property Descriptor for the member.
Example:
In the following illustration, an accessor decorator (@configurable) is utilized on a property of the Employee class.
class Employee {
private _salary: number;
private _name: string;
@configurable(false)
get salary() { return 'Rs. ${this._salary}'; }
set salary(salary: any) { this._salary = +salary; }
@configurable(true)
get name() { return 'Sir/Madam, ${this._name}'; }
set name(name: string) { this._name = name; }
}
4. Property Decorators
A property decorator is specified immediately prior to a property declaration. It resembles method decorators in function. The key distinction between property decorators and method decorators lies in the fact that property decorators do not take a property descriptor as an input and do not yield any return value.
The syntax for the property decorator function takes two parameters. These are:
- Either the class's constructor function for a static property or the class's prototype for an instance property.
- The name of the member.
Example:
In the following example, the @ReadOnly decorator will render the name property immutable, thus preventing any modifications to its value.
class Company {
@ReadOnly
name: string = "TypeScript Tutorial.com";
}
let comp = new Company();
comp.name = 'SSSIT.com'; // Here, we can't change company name.
console.log(comp.name); // 'TypeScript Tutorial.com'
5. Parameter Decorators
A parameter decorator is specified immediately prior to a parameter definition. It is utilized within the function of a class constructor or method definition. Its use is not permitted in declaration files or in any other ambient scenarios (for instance, within a declared class).
The expression for the parameter decorator function accepts three arguments. They are:
- Either the constructor function of the class for a static member or the prototype of the class for an instance member.
- The member name.
- The index of the parameter in the function?s arguments list.
Example:
In the following illustration, a parameter decorator (@required) is utilized on the parameter of a member within the Person class.
class Person {
msg: string;
constructor(message: string) {
this.msg = message;
}
@validate
show(@required name: string) {
return "Hello " + name + ", " + this.msg;
}
}