Quick Tip: Decorators in TypeScript — SitePoint

By admin
In this quick tip, excerpted from Unleashing the Power of TypeScript,

Steve
PERSON

shows you how to use decorators in

TypeScript
ORG

, which is a new feature in

TypeScript 5
LAW

.

Decorators have almost been part of

ECMAScript
ORG

for as long as I can remember. These nifty tools let us modify classes and members in a reusable way. They’ve been on the scene for a while in

TypeScript
ORG

— albeit under an experimental flag. Although the Stage

2
CARDINAL

iteration of decorators was always experimental, decorators have been widely used in libraries like MobX, Angular, Nest, and TypeORM.

TypeScript
ORG

5.0’s decorators are fully in sync with the

ECMAScript
ORG

proposal, which is pretty much ready for prime time, sitting at

Stage 3
EVENT

.

Decorators let us craft a function that tweaks the behavior of a class and its methods. Imagine needing to sneak in some debug statements into our methods. Before

TypeScript
ORG

5.0, we’d have been stuck copying and pasting the debug statements manually in each method. With decorators, we just do the job once and the change will be supported through each method the decorator is attached to.

Let’s say we want to create a decorator for logging that a given method is deprecated:

class

Card { constructor(public
ORG

suit: Suit, public rank: Rank) { this.suit = suit; this.rank = rank; } get name(): CardName { return `${this.rank} of ${this.suit}`; } @deprecated // 👀 This is a decorator!

getValue
PERSON

(): number { if (this.rank === ‘Ace’) return

14
CARDINAL

; if (this.rank === ‘King’) return

13
CARDINAL

; if (this.rank === ‘

Queen
PERSON

‘) return

12
CARDINAL

; if (this.rank === ‘

Jack
PERSON

‘) return

11
CARDINAL

; return

this.rank
DATE

; } // The new way to do it! get value(): number { if (this.rank === ‘Ace’) return

14
CARDINAL

; if (this.rank === ‘King’) return

13
CARDINAL

; if (this.rank === ‘

Queen
PERSON

‘) return

12
CARDINAL

; if (this.rank === ‘

Jack
PERSON

‘) return

11
CARDINAL

; return

this.rank
DATE

; } } const card = new Card(‘Spades’, ‘

Queen
PERSON

‘); card.getValue();

We want a warning message logged to the console whenever card.getValue() is called. We could implement the above decorator as follows:

const deprecated = <This, Arguments extends any[], ReturnValue>( target: (this: This, …args: Arguments) => ReturnValue, context:

ClassMethodDecoratorContext
ORG

< This, (this: This, …args: Arguments) => ReturnValue >, ) => { const methodName = String(context.name); function replacementMethod(this: This, …args: Arguments): ReturnValue { console.warn(`Warning: ‘${methodName}’ is deprecated.`); return target.call(this, …args); } return replacementMethod; };

This might look a little confusing at

first
ORDINAL

, but let’s break it down:

Our decorator function takes

two
CARDINAL

arguments: target and context .

and . target is the method itself that we’re decorating.

is the method itself that we’re decorating. context is metadata about the method.

is metadata about the method. We return some method that has the same signature.

In this case, we’re calling

console.warn
PERSON

to log a deprecation notice and then we’re calling the method.

The ClassMethodDecorator type has the following properties on it:

kind : the type of the decorated property. In the example above, this will be method , since we’re decorating a method on an instance of a

Card
PRODUCT

.

: the type of the decorated property. In the example above, this will be , since we’re decorating a method on an instance of a . name : the name of property. In the example above, this is

getValue
PERSON

.

: the name of property. In the example above, this is . static : a value indicating whether the class element is a static ( true ) or instance ( false ) element.

: a value indicating whether the class element is a static ( ) or instance ( ) element. private : a value indicating whether the class element has a private name.

: a value indicating whether the class element has a private name. access : an object that can be used to access the current value of the class element at runtime.

: an object that can be used to access the current value of the class element at runtime. has : determines whether an object has a property with the same name as the decorated element.

: determines whether an object has a property with the same name as the decorated element. get : invokes the setter on the provided object.

You can kick the tires of the code samples above in this playground.

Decorators provide convenient syntactic sugar for adding log messages — like we did in the example above — as well as a number of other common use cases. For example, we could create a decorator that automatically binds the method to the current instance or that modifies the property descriptor of the method or class.

This article is excerpted from Unleashing the Power of TypeScript, available on

SitePoint Premium
ORG

and from ebook retailers.