Typescript is a superset of Javascript. Typescript allows Javascript developers to add static type-checking and other modern features to plain Javascript. Some of these extra features do get added to the standard Javascript(ECMAScript). The concept of Decorators is one of those extra features. In this article, we would see what Decorators are and how they can be used in Typescript. What are Decorators? Decorators are used to add special behaviors to a class or to members of a class. The decorator is a function that takes in arguments and adds some annotation or metadata to the arguments in a declarative way. Decorators are prefixed with an . The value after the must evaluate to a function that has information about the decorated declaration. @ @ Decorators can be attached to a class declaration, method, property, accessor, or parameter. Decorators are currently a in Javascript, but they are available as an experimental feature in Typescript. To use decorators, we have to set the compiler option in the file. stage 2 proposal experimentalDecorators tsconfig.json Decorator Factories Since decorators are functions, we might need to pass in some additional options so as to customize how the decorator works. To do this, we make use of Decorator Factories. A is simply a function that returns a function. This returned function would then implement the decorator. Decorator Factory const decoratorFactory = (value: string) => { // the decorator factory returns a decorator function return (target: any) => { // the returned decorator uses 'target' and 'value' } } Class Decorators A is used to decorate a class declaration. The class decorator receives the constructor of the class as its only argument. The decorator can be used to observe, modify, or replace a class definition. Class Decorator const classDecorator = (constructor: Function) => { // do something with your class } @classDecorator class Person {} The example below adds a property to the class by returning a new class that extends the constructor passed into the decorator and adds the property. const addAgeToPerson = <T extends { new (...args: any[]): {} }>( originalConstructor: T ) => { return class extends originalConstructor { age: number; constructor(...args: any[]) { super(); this.age = 28; } }; }; @addAgeToPerson class Person {} const person = new Person(); console.log(person.age); // 28 Property Decorators A is used to decorate a property of a class. The property decorator receives two arguments. Property Decorator The prototype of the class for an instance member OR the constructor function of the class for a static member. The name of the property. const propertyDecorator = (target: any, propertyName: string) => { // do something with your property } class Person { @propertyDecorator age = 28 } Property decorators can be used to override the property being decorated. This can be done with the static method and a new setter and getter for the property. Object.defineProperty In the example below, we are going to prevent the age from being changed to a value lower than 18. const propertyDecorator = (target: any, propertyName: string) => { let currentAge: number = target[propertyName]; Object.defineProperty(target, propertyName, { set: (newAge: number) => { if (newAge < 18) { return; } currentAge = newAge; }, get: () => currentAge }); } class Person { @propertyDecorator age = 28 } const person = new Person(); console.log(person.age); // 28 person.age = 16; console.log(person.age); // 28 person.age = 24; console.log(person.age); // 24 Accessor Decorators An is used to decorate an accessor property of a class. The accessor decorator gets applied to the for that accessor. Accessor Decorator Property Descriptor Since the combines both the and accessor, all decorators for the member must be applied to one accessor, not to both of the accessors. Property Descriptor get set The accessor decorator receives three arguments. The prototype of the class for an instance member OR the constructor function of the class for a static member. The name of the accessor. The for the accessor. Property Descriptor const accessorDecorator = (target: any, memberName: string, descriptor: PropertyDescriptor) => { // do something with your accessor console.log('Accessor decorator!'); console.log(target); console.log(memberName); console.log(descriptor); } class Product { title: string; private _price: number; @accessorDecorator get price() { return this._price } set price(val: number) { if (val > 0) { this._price = val; } else { throw new Error('Price cannot be lower than zero!'); } } constructor(t: string, p: number) { this.title = t; this._price = p; } } When the above code runs, it produces the output below. As seen from the result, the decorator receives both the and accessor in the despite being added to just one of the accessors. set get Property Descriptor, The value returned from the accessor decorator would be used as the new for the member. Property Descriptor In the example below, we are using a decorator factory to change the configurable value of the accessor. const configurable = (value: boolean) => { return (target: any, memberName: string, descriptor: PropertyDescriptor) => { descriptor.configurable = value; } } class Product { title: string; private _price: number; @configurable(false) get price() { return this._price } set price(val: number) { if (val > 0) { this._price = val; } else { throw new Error('Price cannot be lower than zero!'); } } constructor(t: string, p: number) { this.title = t; this._price = p; } } Method Decorators A is used to decorate a class method. The method decorator is applied to the for that method. Method Decorator Property Descriptor The method decorator receives three arguments. The prototype of the class for an instance member OR the constructor function of the class for a static member. The name of the method. The for the method. Property Descriptor const methodDecorator = (target: any, methodName: string, descriptor: PropertyDescriptor) => { // do something with your method console.log('Method decorator!'); console.log(target); console.log(methodName); console.log(descriptor); } class Product { title: string; private _price: number; constructor(t: string, p: number) { this.title = t; this._price = p; } @methodDecorator getPriceWithDiscount(discount: number) { return this._price - (this._price * discount) / 100; } } When the above code runs, it produces the output below. The value returned from the accessor decorator would be used as the new for the member. Property Descriptor In the example below we would use a method decorator to add a deprecated warning to our method. We do this by returning a wrapper function that wraps around the original method and adds the new functionality. This wrapper function is then placed in a getter function and returned as the new . Property Descriptor const methodDecorator = (target: any, methodName: string, descriptor: PropertyDescriptor) => { const originalMethod = descriptor.value; // new property descriptor const newDescriptor: PropertyDescriptor = { configurable: true, enumerable: false, // getter function get() { // wrapper function const newMethod = (...args: any[]) => { console.warn(`Method ${methodName} is deprecated`); return originalMethod.apply(this, args) } return newMethod; } }; return newDescriptor; } class Product { title: string; private _price: number; constructor(t: string, p: number) { this.title = t; this._price = p; } @methodDecorator getPriceWithDiscount(discount: number) { return this._price - (this._price * discount) / 100; } } const prod = new Product("shoes", 100) console.log(prod.getPriceWithDiscount(20)) The code above produces the result below. Parameter Decorators A is is used to decorate a parameter of a class method or a class constructor. Parameter Decorator The parameter decorator receives three arguments. The prototype of the class for an instance member OR the constructor function of the class for a static member. The name of the method that uses the parameter. The index of the parameter in the function’s parameter list. const parameterDecorator = (target: any, methodName: string, position: number) => { // do something with your parameter console.log('Parameter decorator!'); console.log(target); console.log(methodName); console.log(position); } class Product { title: string; private _price: number; constructor(t: string, p: number) { this.title = t; this._price = p; } getPriceWithDiscount(@parameterDecorator discount: number) { return this._price - (this._price * discount) / 100; } } When the above code runs, it produces the output below. Conclusion Decorators are functions that add special behaviors to a class or members of a class To customize how a decorator works, use a decorator factory which allows you to pass in additional options Examples of libraries that make use of Decorators include , , and . Angular Typegoose Nestjs