Typescript: A Review On Decorators

Introduction

If you like to use an object oriented approach to writing code in javascript, and use typescript to compile said javascript, you should learn about decorator functions. As I continue to learn typescript I started to notice odd ‘at’ symbols when examining typescript code that compiles to class based javascript. Upon further research I found that these were decorator functions. Not only was their syntax peculiar but these functions never seemed to be called directly. Also the order in which they were executed seemed muddled at best. In this article we will go into what exactly a decorator function is followed by a review of various types of decorator functions. But before I delve into decorators it is important to mention the need to adjust your typescript settings in tsconfig.json (like I wrote about in a previous article) to allow for the use of decorator functions. Within tsconfig.json you need to set:

"target": "es2016",

This enables javascript classes. You will also need to adjust your experimentalDecorators setting to allow for Decorators. Do so by uncommenting/changing the value of the following:

"experimentalDecorators": true,

Now lets learn some things about Decorators so we have a better idea of what these options allow us to do.

The Various Types Of Decorator Functions

Before I begin my list of decorators I’d like to review a bit of the ‘ground rules’ that apply to these functions. As previously mentioned they are generally never called directly. Instead they are prefaced with an @ symbol which serves as a pointer to your decorator function. Because they are never directly called they are instead executed wherever they are referenced and in the order of whichever decorator function is closest to what they are referencing up (think bubbling). Also, from what I have seen it is convention that decorator functions begin with a capital letter. Now that we know some basics lets review the types of decorator functions:

Class Decorators
The identifier for these functions reside just above class definitions, thus they are executed whenever a class is defined. These decorators only receive one argument by default which is that of the contructor argument:

  • constructor — which is of type function, if this is logged it will log the whole class due to classes just being syntactical sugar added onto constructor functions
An example of a Class Decorator definition

Decorator Factories
The identifiers for these functions can reside anywhere and will reference the nearest code below it. These decorator functions are special in that you can pass arguments into them and they will return a second decorator function which will be of the type to which you reference based on where you put the identifier for this function. There is also a bubbling effect in that the first function in the Decorator Factory will execute (because it is the only decorator function I’ve come across that is actually called directly), followed by whatever decorators are below it. After the decorators below this decorator factory are executed, the second returned function within this one will execute. I’ll have to illustrate this in several pictures:

An example of a Decorator Factory definition that returns a custom Class Decorator

Lets use this with the aforementioned LogClass class decorator so we can observe the order to which these are executed:

Notice the order: CreateDecoratorFn will execute the first part of its function which should just be logging DECORATOR FACTORY. Then our LogClass decorator will execute, then the returned function of our CreateDecoratorFn should execute

Lets check out some dev tools to see the order in which this executes!

As we can see, first DECORATOR FACTORY is logged which is the first part of our CreateDecoratorFn function, then our LogClass function is logged, then the returned function of our CreateDecoratorFn is logged.

Property Decorators
The identifiers for these decorator functions are added just above a classes property and receive target and propertyName by default. Here are what these arguments mean:

  • target — this can be either the Prototype if the property decorator references an instance property or the class constructor if it references a static property.
  • propertyName — this is a string or symbol that references the property identifier.

An example of a Property Decorator would be:

An example of a property decorator definition

And an example of its usage would be:

this would log the Prototype of the Dog object along with the string of ‘age’

Accessor Decorators
The identifiers for these decorator functions reside just above getter or setter methods. They receive three default arguments which are target, name, and descriptor. Here are the details of each of these arguments:

  • target — which is of type Prototype for an instance method or of the classes constructor function for a static method.
  • name — which is a string that is the name of our accessors getter or setter method that we reference.
  • descriptor — which is of a type exclusive to typescript called PropertyDescriptor

An example of an Accessor Decorator would be:

and an example of its usage would be:

This would log the Prototype, a string of ‘name’, and a PropertyDescriptor object

Method Decorators
These decorator functions are much like the aforementioned Accessor Decorators, but their identifiers reside above normal instance or static methods. They have three default arguments as well, but pay attention to the subtle change specific to the name argument. These three default arguments again are target, name, and descriptor. Here are their details:

  • target — which is of type Prototype for an instance method or of the classes constructor function for a static method.
  • name — which is either a string or a symbol that identifies the method we reference.
  • descriptor — which again is of the aforementioned PropertyDescriptor type.

An example of a Method Decorator would be:

An example of using a Method Decorator would be:

Please ignore the identifier for the LogParameter decorator. We will go over that next! This would log the Prototype, a string of ‘command’, and the PropertyDescriptor object.

Parameter Decorators
The identifiers for parameter decorator reside to the left of parameters (or arguments) in a method. The default arguments for this decorator function are target, name, and position. Here are the details of these arguments:

  • target — this will depend on where the identifier for this function resides. The target will be a Prototype for the object if referenced inside and instance method, or the classes constructor function if referenced inside a static method.
  • name — this will be either a string or a Symbol that will be equal to the name of the method we place the identifier for this function in.
  • position — this will be of type number and will reference the placement of the parameter we reference inside of the method. The counting will start at 0 for the first parameter and increment by one after each comma.

An example of a Parameter Decorator would be:

and an example of using a Parameter Decorator would be:

This would log the Prototype, a string of ‘command’, and the number 1 since the trick parameter we are referencing is the second argument.

Conclusion

Decorator functions provide some interesting and useful behavior that allow us to expand the functionality of our classes, methods, and properties. By giving us access to various abstract parts of our classes, we can write very specific and fine tuned code. Hopefully this article has shed some light on a handy tool that some of you may implement in the future.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store