Understanding TypeScript decorators.
Tags: typescript, javascript, tutorial
Published: 2021-09-29 08:57:44

JavaScript is an awesome programming language. And TypeScript has done a great job at filling in the gaps of JavaScript. Not only does it add types, it also implements a few extra features which aren't there in JavaScript _yet_. One of them are decorators.

## What is a decorator?

Decorators have been there in programming languages for a long time. Definitions vary, but in short a decorator is a pattern in JavaScript which is used to wrap something to change it's behavior.

In both JavaScript and TypeScript this is an experimental feature. In JavaScript, it's still a Stage 2 proposal and you can only use it via transpilers like Babel.

I've decided to explain TypeScript decorators because in TypeScript it has been standardized, and both are basically the same anyways.

## Using Decorators

This is a very simple example of how to use a decorator:

```ts
const myDecorator = (thing: Function) => {
	// return something
}

@myDecorator
class Thing {

}
```

First we define a function `myDecorator`, and then we "decorate" a variable (our class `Thing` in this case) with the decorator. The decorator can return pretty much anything, but most of the time it used to set properties on the class, etc. Here's a real life sample:

```ts
const defaultGun = (gun: Function) => class extends gun {
	ammo = 10
}

@defaultGun
class Gun {

}
```

Now, `Gun` will have a `ammo` property by default.

```ts
const gun = new Gun()
console.log(gun.ammo) // => 10
```

### Decorating functions

Another place we can use decorators is in class methods. This time, the decorator gets three arguments.

```ts
const myDecorator = (parent: Function, prop: string, descriptor: PropertyDescriptor) => {
	// return something
}

class Gun {
	@myDecorator
	fire() {
		console.log('Firing in 3... 2... 1... 🔫')
	}
}
```

The first param contains the class where the decorated thing exists (in our case `Gun`). The second param is the name of the property decorated (in our case `fire`). The last is the property descriptor, which is the output of [`Object.getOwnPropertyDescriptor(parent[prop])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor)

### Properties

You can also decorate properties. It is pretty much the same as function decorators, except there is no third parameter:

```ts
const myDecorator = (parent: Function, prop: string) => {
	// return something
}
```

### More places to decorate

You can also decorate in more places. Check out the [documentation](https://www.typescriptlang.org/docs/handbook/decorators.html) to learn more.

## Use cases

There are many uses for decorators. We'll go over some here.

### Calculate performance

```ts
class Gun {
	@time
	fire() {
		console.log('Firing in 3... 2... 1... 🔫')
	}
}
```

`time` could be a function which calculates the execution time.

### Decorator factory

Decorators can also be factories, which returns a function which is the actual decorator. This can be useful when you want your decorators need any arguments.

```ts
// The decorator factory
const change = value => {
	// The factory will return a new handler
	return (target, prop) => {
		// We replace the old value with a new one
		Object.defineProperty(target, prop, {value})
	}
}
```

Then when "decorating" we just need to decorate like a function:

```ts
class Gun {
	@change(20)
	ammo = 10
}

const gun = new Gun();
console.log(gun.ammo) // => 20
```

## A practical example: error handling

Let's use what we have learned to solve a real world problem.

```ts
class Gun {
	ammo = 0

	fireTwice() {
		console.log('Firing in 3... 2... 1... 🔫')
	}
}
```

To fire twice, we need at least 2 ammo. We can make a check for that using a decorator:

```ts
const minAmmo = (ammo: number) => (
	target: Object,
	prop: string,
	descriptor: PropertyDescriptor
) => {
	const original = descriptor.value;

	descriptor.value = function (...args) {
		if (this.ammo >= ammo) original.apply(this);
		else console.log('Not enough ammo!');
	}

	return descriptor;
}
```

`minAmmo` is a factory decorator which takes a parameter `ammo` which is the minimum ammo needed.

We can use implement it in our `Gun` class.

```ts
class Gun {
	ammo = 0

	@minAmmo(2)
	fireTwice() {
		console.log('Firing in 3... 2... 1... 🔫')
	}
}
```

Now if you run `fireTwice()`, it won't fire because we don't have enough ammo.

The nice thing is that we can just reuse this without rewriting an if statement. Suppose we need a `fireOnce` method. We can easily implement that.

```ts
class Gun {
	ammo = 0

	@minAmmo(2)
	fireTwice() {
		console.log('Firing twice in 3... 2... 1... 🔫')
	}

	@minAmmo(1)
	fireOnce() {
		console.log('Firing once in 3... 2... 1... 🔫')
	}
}
```

This kind of decorator can be really useful authentication. authorization, and all the other good stuff.

---

Liked the post? :heart: it. Loved it? :unicorn: it.

If you want more people to learn about this, [share this on Twitter](https://twitter.com/intent/tweet?text=Decorators%20are%20one%20confusing%20part%20of%20TypeScript%2C%20since%20it%20is%20something%20which%20doesn't%20exist%20yet%20in%20JavaScript.%20Sound%20Familiar%3F%20Read%20this%20post%20by%20%40SiddharthShyniben%20to%20learn%20more%20about%20Decorators&url=https%3A%2F%2Fdev.to%2Fsiddharthshyniben%2Funderstanding-typescript-decorators-3ifc)