TypeScript Functional Decorators w/ usecases

Search for a command to run...

No comments yet. Be the first to comment.
Cases and their uses.

Console API of Javascript scripts provide a lot more than just console.log. Here are some of my observations & learning on the Console API. Also, a few tips that I use for logging are added at the end - Feel free to stick till the end. 1. console.log...

Have you ever wondered, how would your webpage look when printed? 🤔 And you don't have a printer? or you don't want to waste a ton of papers for development? Chrome dev tools got you covered! Following are the steps to check your print preview in Ch...

According to MDN, NaN is a property of the global object. In other words, it is a variable in global scope. The initial value of NaN is Not-A-Number — the same as the value of Number.NaN. In modern browsers, NaN is a non-configurable, non-writable ...

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.
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.
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.