Understanding TypeScript Decorators
Tags: programming, typescript
Published: 2021-04-16 17:51:00

---
title: Understanding TypeScript Decorators
published: true
date: 2021-04-16 17:49:53 UTC
tags: Programming,Typescript
canonical_url: https://shakil.me/understanding-typescript-decorators/
---

 ![Understanding TypeScript Decorators](https://images.unsplash.com/photo-1599256630445-67b5772b1204?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8c2VhcmNofDh8fHdyZW5jaHxlbnwwfHx8fDE2MTg1OTM0MDM&ixlib=rb-1.2.1&q=80&w=2000)

Decorators are used to modify classes and its members (methods, properties).  
The question may be, **why do we need decorators to change those things? Why not directly change in the declarations?**

Directly changing is not scalable. If you need the same changes in 100 classes, you have to write it 100 times. Using decorators, you write it once and apply it as many times as you want.

Decorators give you a central point of change which easier to work with. Frameworks like `NestJS`, `Angular` use decorators extensively.

In TypeScript, you can decorate the following things:

- class
- method
- property
- accessor
- parameter

_Decorators are a TypeScript feature and are not officially included in JavaScript yet. However, they are likely to be added in the future (Currently in stage 2)._

### Class decorator

```Typescript
// The following decorator makes any class more Funky
function makeFunky(target: Function) {
	return class extends target {
		constructor() {
			super()
		}
		funkIt() {
			console.log("Get The Funk Outta My Face")
			}
		}
	}
}

@makeFunky
class Car {
	wheels = 4
	state = 'pause'
	drive() {
		this.state = 'running'
	}
}

const car = new Car()
car.funkIt()
// logs: Get The Funk Outta My Face
```

Decorators are just functions, `makeFunky` is the decorator above. `makeFunky` gets the class `Car` it's applied to as a parameter. It returns a new class which is just a modified version of the original class.

Decorators are called when the class is declared—not when an object is instantiated.

### Method decorator

Decorators applied to a method gets the following parameters

- **target** : for static methods, `target` is the constructor function of the class. For an instance method, `target` is the prototype of the class.
- **propertyKey** : The name of the method.
- **descriptor** : The Property Descriptor for the method

```javascript
function log(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
	const originalMethod = descriptor.value
	descriptor.value = function () {
		console.log('arguments are ', arguments)
		const result = originalMethod.apply(this, arguments)
		console.log(`result is ${result}`)
		return result
	}
}

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

const calc = new Calculator()
calc.add(10, 20)

/*
[LOG]: "arguments are ",  {
  "0": 10,
  "1": 20
} 
[LOG]: "result is 30" 
*/
```
### Property decorator

Similar to method decorator. Gets the same parameters `target`, `propertyKey`, and `descriptor` as the method decorators. Its return value is ignored.

```Typescript
function logMutation(target: any, key: string ) {
	var _val = target[key]
	Object.defineProperty(target, key, {
        set: function(newVal) {
            console.log('new value is ' + newVal)
            _val = newVal
        },
        get: function() {
                return _val
            }
        })
}

class Person {
    @logMutation
	public age: number

    constructor(age: number) {
        this.age = age
    }
}

const jack = new Person(20)
jack.age = 40

/*
[LOG]: "new value is 20" 
[LOG]: "new value is 40" 
*/
```

### Accessor and parameter decorators

Accessor decorators are same as method decorators, but they are applied to either the getter or setter method of a single member.

Parameter decorators are applied on, you got it, parameters.

## Decorator Factory

A common pattern in decorator usage is calling a function that returns a decorator. Here's the previously mentioned method decorator `log` , returned by invoking the function logger

```Typescript
function logger(functionName: string) {
    function log(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
        const originalMethod = descriptor.value
        descriptor.value = function () {
            console.log('method name: ' + functionName)
            console.log('arguments are ', arguments)
            const result = originalMethod.apply(this, arguments)
            console.log(`result is ${result}`)
            return result
        }
    }
    return log
}

class Calculator {
	@logger('add')
	add(x: number, y: number): number {
		return x + y
	}
}

const calc = new Calculator()
calc.add(10, 20)

/*
[LOG]: "method name: add" 
[LOG]: "arguments are ", {
  "0": 10,
  "1": 20
} 
[LOG]: "result is 30" 
*/
```

## Decorator in the wild

Some use cases of decorators as being utilized by popular libraries/frameworks.

- **NestJS** uses the class decorator `@Controller('pathName')` to define a class as a controller. Decorators associate classes with required metadata and enable Nest to create a routing map (tie requests to the corresponding controllers). It also uses decorators to define modules, injectable instances for its dependency injection system.
- **TypeORM** uses decorators to define a class as an entity `@Entity()`, tagging properties as columns `@Column()`, auto-incremented ID etc.
- The NPM package **class-validator** is used for validating properties in an object (used in validations such as income data, arguments of a function). It has decorators like `@IsInt()` `@Min(5)` `@Max(25)` to set restrictions on a particular field.