Types of Decorators in Typescript
Decorators in TypeScript are used in the following ways:
- Class Decorators
- Method Decorators
- Accessor Decorators
- Property Decorators
- Parameter Decorators
Class Decorators
A class decorator speaks about the class behaviours and is defined shortly before the class declaration. The constructor of the class is decorated with a class decorator. A class decorator can be used to look at, change, or replace the definition of a class. If the class decorator returns a value, the specified function Object() { [native code] } function will be used to replace the class declaration.
Example
function SelfDriving(constructorFunction: Function) {
console.log('— the decorator function has been called —');
constructorFunction.prototype.selfDrivable = true;
}
@SelfDriving
class Car {
private _make: string;
constructor(make: string) {
console.log('This constructor was called —');
this._make = make;
}
}
console.log('— establishing a new instance -');
let car: Car = new Car("Thar");
console.log(car);
console.log(`selfDriving: ${car['selfDrivable']}`);
console.log('— adding one more instance —');
car = new Car("Bugati");
console.log(car);
console.log(`selfDriving: ${car['selfDrivable']}`);
Output:

Method Decorators
Just before a method declaration, a method decorator is defined. It's applied to the method's property descriptor. It can be used to look at, change, or replace the definition of a method. In a declaration file, we can't use the method decorator.
The method decorator function takes three arguments in its expression. They are as follows:
- For a static member, the class's constructor function or its prototype for an instance member.
- The name of a member.
- This is the member's Property Descriptor.
Example
function Enumerable(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("-- target --");
console.log(target);
console.log("-- proertyKey --");
console.log(propertyKey);
console.log("-- descriptor --");
console.log(descriptor);
//create an enumerable method
descriptor.enumerable = true;
}
class Car {
@Enumerable
run() {
console.log("inside the run method");
}
}
console.log("-- creating instance --");
let car = new Car();
console.log("-- looping --");
for (let key in car) {
console.log("key: " + key);
}
Output

Accessor Decorators
Just before an accessor declaration, an Accessor Decorator is specified. It is applied to the accessor's property descriptor. It can be used to inspect, change, or replace the definitions of an accessor.
The accessor decorator function takes three arguments in its statement. They are as follows:
- Either the class's constructor function for a static member of the class's prototype for an instance member.
- The name of the member.
- The member's Property Descriptor.
Example
function Enumerable(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
//enumerable method
descriptor.enumerable = true;
}
class Person {
_name: string;
constructor(name: string) {
this._name = name;
}
@Enumerable
get name() {
return this._name;
}
}
console.log("-- creating instance --");
let person = new Person("Diana");
console.log("-- looping --");
for (let key in person) {
console.log(key + " = " + person[key]);
}
Output

Property Decorators
Just before a property declaration, a property decorator is defined. It's analogous to the decorators for methods. Property decorators vary from method decorators in that they don't take a property descriptor as an input and don't return anything.
The property decorator function takes two arguments in its statement. They are as follows:
- For a static member, the class's constructor function or its prototype for an instance member.
- The name of a member.
Example
In the example below, the @ReadOnly decorator makes the name property read-only, preventing us from changing its value.
const mylistOnly = (mylist: string[]) => {
return (target: any, memberName: string) => {
let currentValue: any = target[memberName];
Object.defineProperty(target, memberName, {
set: (newValue: any) => {
if (!mylist.includes(newValue)) {
return;
}
currentValue = newValue;
},
get: () => currentValue
});
};
}
class Person {
@mylistOnly(["Hemsworth", "Parker"])
name: string = "Hemsworth";
}
const person = new Person();
console.log(person.name);
person.name = "John";
console.log(person.name);
person.name = "Parker";
console.log(person.name);
Output:

Parameter Decorators
Just before a parameter declaration, a parameter decorator is defined. It's used for a class function Object() { [native code] } or method declaration function. It can't be utilised in a declaration file or anywhere else in the environment (such as in a declared class).
The parameter decorator function accepts three arguments in its expression. They are as follows:
- For a static member, the class's constructor function or its prototype for an instance member.
- The name of a member.
- The parameter's index is in the function's arguments list.
Example
A parameter decorator (@required) is applied to a parameter of a member of the Person class in the example below.
function notNull(target: any, propertyKey: string, parameterIndex: number) {
console.log("The notNull method of the param decorator has been called. ");
Validator.registerNotNull(target, propertyKey, parameterIndex);
}
function validate(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("The validate function of the method decorator has been called. ");
let originalMethod = descriptor.value;
//wrapup the actual method
descriptor.value = function (...args: any[]) {//wrap methop
if (!Validator.performValidation(target, propertyKey, args)) {
console.log("Validation failure so method aborted: " + propertyKey);
return;
}
let result = originalMethod.apply(this, args);
return result;
}
}
class Validator {
private static notNullValidatorMap: Map<any, Map<string, number[]>> = new Map();
//more validator maps should be included
static registerNotNull(target: any, methodName: string, paramIndex: number): void {
let paramMap: Map<string, number[]> = this.notNullValidatorMap.get(target);
if (!paramMap) {
paramMap = new Map();
this.notNullValidatorMap.set(target, paramMap);
}
let paramIndexes: number[] = paramMap.get(methodName);
if (!paramIndexes) {
paramIndexes = [];
paramMap.set(methodName, paramIndexes);
}
paramIndexes.push(paramIndex);
}
static performValidation(target: any, methodName: string, paramValues: any[]): boolean {
let notNullMethodMap: Map<string, number[]> = this.notNullValidatorMap.get(target);
if (!notNullMethodMap) {
return true;
}
let paramIndexes: number[] = notNullMethodMap.get(methodName);
if (!paramIndexes) {
return true;
}
let hasErrors: boolean = false;
for (const [index, paramValue] of paramValues.entries()) {
if (paramIndexes.indexOf(index) != -1) {
if (!paramValue) {
console.error("at index method param " + index + " cannot be null");
hasErrors = true;
}
}
}
return !hasErrors;
}
}
class Task {
@validate
run(@notNull name: string): void {
console.log("running task, name: " + name);
}
}
console.log("-- creating instance --");
let task: Task = new Task();
console.log("-- calling Task#run(null) --");
task.run(null);
console.log("----------------");
console.log("-- calling Task#run('test') --");
task.run("test");
Output

Frequently Asked Questions
1. How do we perform Decorator evaluation?
Ans: The order in which decorators are applied to various declarations within a class is well defined:
- For each instance member, parameter decorators are applied first, followed by Method, Accessor, or Property Decorators.
- For each static member, parameter decorators are applied first, followed by Method, Accessor, or Property Decorators.
- For the constructor, parameter decorators are used.
- The class is decorated with class decorators.
2. What is a Decorator?
Ans: A decorator is a type of declaration that can be added to a class declaration, a method, an accessor, a property, or a parameter. Decorators have the form @expression, where expression must evaluate to a function that will be called with information about the decorated declaration when it is called at runtime.
For example, given the decorator @sealed, the sealed function could be written as follows:
function sealed(target) {
// perform something for target
}
Key Takeaway
We have discussed decorators in typescript, used them with classes, and learnt the differences between them in this article. We can now start developing our decorators to reduce boilerplate code in our codebase, or we can utilise decorators more confidently with libraries like Mobx.
You can take a look at our Typescript archives section and see many more interesting topics related to it.
Apart from that, you can refer to our guided paths on Coding Ninjas Studio to learn more about DSA, Competitive Programming, JavaScript, and System Design.
Happy Learning!