On this fast tip, excerpted from Unleashing the Energy of TypeScript, Steve exhibits you the best way to use decorators in TypeScript, which is a brand new function in TypeScript 5.
Decorators have virtually been a part of ECMAScript for so long as I can bear in mind. These nifty instruments allow us to modify courses and members in a reusable manner. They’ve been on the scene for some time in TypeScript — albeit underneath an experimental flag. Though the Stage 2 iteration of decorators was at all times experimental, decorators have been extensively utilized in libraries like MobX, Angular, Nest, and TypeORM. TypeScript 5.0’s decorators are absolutely in sync with the ECMAScript proposal, which is just about prepared for prime time, sitting at Stage 3.
Decorators allow us to craft a perform that tweaks the habits of a category and its strategies. Think about needing to sneak in some debug statements into our strategies. Earlier than TypeScript 5.0, we’d have been caught copying and pasting the debug statements manually in every methodology. With decorators, we simply do the job as soon as and the change can be supported by every methodology the decorator is connected to.
Let’s say we need to create a decorator for logging {that a} given methodology is deprecated:
class Card {
constructor(public swimsuit: Swimsuit, public rank: Rank) {
this.swimsuit = swimsuit;
this.rank = rank;
}
get title(): CardName {
return `${this.rank} of ${this.swimsuit}`;
}
@deprecated // 👀 This can be a decorator!
getValue(): quantity {
if (this.rank === 'Ace') return 14;
if (this.rank === 'King') return 13;
if (this.rank === 'Queen') return 12;
if (this.rank === 'Jack') return 11;
return this.rank;
}
// The brand new method to do it!
get worth(): quantity {
if (this.rank === 'Ace') return 14;
if (this.rank === 'King') return 13;
if (this.rank === 'Queen') return 12;
if (this.rank === 'Jack') return 11;
return this.rank;
}
}
const card = new Card('Spades', 'Queen');
card.getValue();
We wish a warning message logged to the console every time card.getValue()
is named. We might implement the above decorator as follows:
const deprecated = <This, Arguments extends any[], ReturnValue>(
goal: (this: This, ...args: Arguments) => ReturnValue,
context: ClassMethodDecoratorContext<
This,
(this: This, ...args: Arguments) => ReturnValue
>,
) => {
const methodName = String(context.title);
perform replacementMethod(this: This, ...args: Arguments): ReturnValue {
console.warn(`Warning: '${methodName}' is deprecated.`);
return goal.name(this, ...args);
}
return replacementMethod;
};
This may look somewhat complicated at first, however let’s break it down:
- Our decorator perform takes two arguments:
goal
andcontext
. goal
is the tactic itself that we’re adorning.context
is metadata in regards to the methodology.- We return some methodology that has the identical signature.
- On this case, we’re calling
console.warn
to log a deprecation discover after which we’re calling the tactic.
The ClassMethodDecorator
sort has the next properties on it:
form
: the kind of the adorned property. Within the instance above, this can bemethodology
, since we’re adorning a way on an occasion of aCard
.title
: the title of property. Within the instance above, that isgetValue
.static
: a worth indicating whether or not the category component is a static (true
) or occasion (false
) component.non-public
: a worth indicating whether or not the category component has a personal title.entry
: an object that can be utilized to entry the present worth of the category component at runtime.has
: determines whether or not an object has a property with the identical title because the adorned component.get
: invokes the setter on the supplied object.
You’ll be able to kick the tires of the code samples above in this playground.
Decorators present handy syntactic sugar for including log messages — like we did within the instance above — in addition to plenty of different widespread use circumstances. For instance, we might create a decorator that routinely binds the tactic to the present occasion or that modifies the property descriptor of the tactic or class.
This text is excerpted from Unleashing the Energy of TypeScript, accessible on SitePoint Premium and from book retailers.