TypeScript Functional Decorators w/ usecases
Tags: typescript, decorators, webdev
Published: 2020-08-07 12:22:49

> Generally, decorators in programming are used to add some additional meaning/function to a method, variable or a class.  
---
  
I have used a lot of decorators while working in Spring Boot. I am currently learning Typescript and decorators fascinated me. Here is a small illustration of my use case for typescript decorators for functions.

Consider a Human class which maintains the stamina attribute and it is manipulated by run and rest methods. We need to capture the value of stamina before and after both, the actions - run and rest. I have added snippets for the code with and without decorators.

### Code Before Decorators
```typescript
class Human {
	stamina: number;
	constructor(stamina: number) {
		this.stamina = stamina;
	}

	run(requiredStamina: number): number {
		return (this.stamina -= requiredStamina);
	}

	rest(addedStamina: number): number {
		return (this.stamina += addedStamina);
	}
}

const human = new Human(10);
console.log(`Stamina before run: ` + human.stamina);
human.run(1);
console.log(`Stamina after run: ` + human.stamina);
console.log(`Stamina before rest: ` + human.stamina);
human.rest(12);
console.log(`Stamina after rest: ` + human.stamina);
console.log(`Stamina before run: ` + human.stamina);
human.run(20);
console.log(`Stamina after run: ` + human.stamina);
// Stamina before run: 10
// Stamina after run: 9
// Stamina before rest: 9
// Stamina after rest: 21
// Stamina before run: 21
// Stamina after run: 1
```

The above code looks good but soon the file would be cluttered with boilerplates.

### Code with Decorators
```typescript
class Human {
	stamina: number;
	constructor(stamina: number) {
		this.stamina = stamina;
	}

	@log
	run(requiredStamina: number): number {
		return (this.stamina -= requiredStamina);
	}

	@log
	rest(addedStamina: number): number {
		return (this.stamina += addedStamina);
	}
}
function log(
	target: Object,
	propertyKey: string,
	descriptor: TypedPropertyDescriptor<any>
) {
	const originalMethod = descriptor.value; // save a reference to the original method

	// NOTE: Do not use arrow syntax here. Use a function expression in
	// order to use the correct value of `this` in this method (see notes below)
	descriptor.value = function (...args: any[]) {
		// pre
		console.log(`Stamina before ${propertyKey}: ` + args);
		// run and store result
		const result = originalMethod.apply(this, args);
		// post
		console.log(`Stamina after ${propertyKey}: ` + result);
		// return the result of the original method (or modify it before returning)
		return result;
	};

	return descriptor;
}

const human = new Human(10);
human.run(1);
human.rest(12);
human.run(20);
// Stamina before run: 1
// Stamina after run: 9
// Stamina before rest: 12
// Stamina after rest: 21
// Stamina before run: 20
// Stamina after run: 1
```

I have added comments in the snippet explaining the flow of data in the decorator! Simply put, decorators are invoked before the function is invoked and also has access to the arguments and the return value which could be used by the decorator.

There an N use cases for decorators in general. Let me know some interesting use case in the comment or reach out to me at [radnerus93](https://twitter.com/radnerus93).