• Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar
  • Skip to footer

Codemotion Magazine

We code the future. Together

  • Discover
    • Events
    • Community
    • Partners
    • Become a partner
    • Hackathons
  • Watch
    • Talks
    • Playlists
    • Edu Paths
  • Magazine
    • Backend
    • Frontend
    • AI/ML
    • DevOps
    • Dev Life
    • Soft Skills
    • Infographics
  • Talent
    • Discover Talent
    • Jobs
    • Manifesto
  • Companies
  • For Business
    • ENEN
    • ITIT
    • ESES
  • Sign in
Home » Backend » Understanding the Role of Decorators in TypeScript
Backend

Understanding the Role of Decorators in TypeScript

Similarly to attributes in C#, or annotations in Java, decorators in TypeScript provide a way of programmatically tapping into the process of defining a class. This is how to do it.

June 9, 2021 by Nathan Rozentals

Understanding the Role of Decorators in TypeScript

Decorators in TypeScript provide a way of programmatically tapping into the process of defining a class.

Remember that a class definition describes the shape of a class, what properties it has, and what methods it defines. When an instance of a class is created, these properties and methods become available on the class instance.

Decorators, however, allow us to inject code into the actual definition of a class, before a class instance has been created. They are similar to attributes in C#, or annotations in Java. 

This article is an excerpt from the book, Mastering Typescript, Fourth Edition by Nathan Rozentals – a comprehensive guide for readers to build enterprise-ready, modular web applications using Typescript 4 and modern frameworks.  

Table Of Contents
  1. Decorator overview 
  2. Decorator setup 
  3. Decorator syntax 
  4. Multiple decorators 
  5. Types of decorators 
  6. Decorator factories 
  7. Summary 

Decorator overview 

In this section, we will take a look at the general setup and syntax of decorators, what you need to do to enable them, and how they are applied to classes. We will also show how multiple decorators can be used at the same time, and then discuss the different types of decorators.

Finally, we’ll take a look at decorator factories, and how we can pass parameters into decorator functions. 

But before diving into our article, don’t miss the video The perfect cocktail: Java + Typescript, where open-sourcer Manuel Carrasco Moñino – with a long history contributions in projects such as Vaadin, Polymer, GWT or Apache – demonstrates how to develop a 100% type-safe enterprise app, including the back-end, the front-end, and even the data in the wire.

Loading the player...

Decorator setup 

Decorators are an experimental feature of the TypeScript compiler and are supported in ES5 and above. In order to use decorators, you need to enable a compile option in the tsconfig.json file. This option is named experimentalDecorators, and needs to be set to true, as follows: 

{ 

    “compilerOptions”: { 

        “target”: “es5”, 

        “module”: “commonjs”, 

        “strict”: true, 

        “experimentalDecorators”: true, 

        “skipLibCheck”: true, 

        “forceConsistentCasingInFileNames”: true 

    } 

} 

Here, we have set the compiler option named experimentalDecorators to true. This will allow the use of decorators within our TypeScript code.  

Decorator syntax 

A decorator is a function that is called with a specific set of parameters. These parameters are automatically populated by the JavaScript runtime, and contain information about the class, method, or property to which the decorator has been applied.

The number of parameters, and their types, determine where a decorator can be applied. To illustrate this syntax, let’s define a class decorator, as follows: 

function simpleDecorator(constructor: Function) { 

    console.log(‘simpleDecorator called’); 

} 

Here, we have a function named simpleDecorator, which has a single parameter named constructor of the function type, which logs a message to the console, indicating that it has been invoked.

This function, due to the parameters that it defines, can be used as a class decorator function and can be applied to a class definition, as follows: 

@simpleDecorator 

class ClassWithSimpleDecorator { 

} 

Here, we have a class named ClassWithSimpleDecorator that has the simpleDecorator decorator applied to it. We apply a decorator using the “at” symbol (@), followed by the name of the decorator function. Running this code will produce the following output: 

simpleDecorator called 

Here, we can see that the simpleDecorator function has been invoked. What is interesting about this code sample, however, is that we have not created an instance of the class named ClassWithSimpleDecorator as yet.

All that we have done is specify the class definition, added a decorator to it, and the decorator has been called by the JavaScript runtime automatically.  

Not having to wait for the creation of an instance of a class tells us that decorators are applied when a class is defined. Let’s prove this theory by creating a few instances of this class, as follows: 

let instance_1 = new ClassWithSimpleDecorator(); 

let instance_2 = new ClassWithSimpleDecorator(); 

console.log(`instance_1 : ${JSON.stringify(instance_1)}`); 

console.log(`instance_2 : ${JSON.stringify(instance_2)}`); 

Here, we have created two new instances of ClassWithSimpleDecorator, named instance_1 and instance_2. We then log a message to the console to output the value of each class instance. The output of this code is as follows: 

simpleDecorator called 

instance_1 : {} 

instance_2 : {} 

Here, we can see that the simpleDecorator function has only been called once, even though we have created two instances of the ClassWithSimpleDecorator class. 

Decorators are only invoked once, when a class is defined. 

Multiple decorators 

Multiple decorators can be applied one after another on the same target. As an example of this, let’s define a second decorator function as follows: 

function secondDecorator(constructor: Function) { 

    console.log(`secondDecorator called`); 

} 

Here, we have a decorator function named secondDecorator, which also logs a message to the console once it has been invoked. We can now apply both simpleDecorator (from our earlier code snippet) and secondDecorator as follows: 

@simpleDecorator 

@secondDecorator 

class ClassWithMultipleDecorators { 

} 

Here, we have applied both decorators to a class named ClassWithMultipleDecorators. The output of this code is as follows: 

secondDecorator called 

simpleDecorator called 

Here, we can see that both of the decorators have logged a message to the console. What is interesting, however, is the order in which they are called.  

Decorators are called in the reverse order of their appearance within our code. 

Types of decorators 

Decorators, as mentioned earlier, are functions that are invoked by the JavaScript runtime when a class is defined. Depending on what type of decorator is used, these decorator functions will be invoked with different arguments. Let’s take a quick look at the types of decorators, which are: 

  • Class decorators – These are decorators that can be applied to a class definition. 
  • Property decorators – These are decorators that can be applied to a property within a class.  
  • Method decorators – These are decorators that can be applied to a method on a class. 
  • Parameter decorators – These are decorators that can be applied to a parameter of a method within a class. 

As an example of these types of decorators, consider the following code: 

function classDecorator( 

    constructor: Function) {} 

function propertyDecorator( 

    target: any,  

    propertyKey: string) {} 

function methodDecorator( 

    target: any,  

    methodName: string,  

    descriptor?: PropertyDescriptor) {} 

function parameterDecorator( 

    target: any,  

    methodName: string,  

    parameterIndex: number) {} 

Here, we have four functions, each with slightly different parameters.  

  • The first function, named classDecorator, has a single parameter named constructor of the function type. This function can be used as a class decorator. 
  • The second function, named propertyDecorator, has two parameters. The first parameter is named target, and is of the any type. The second parameter is named propertyKey and is of the string type. This function can be used as a property decorator. 
  • The third function, named methodDecorator, has three parameters. The first parameter, named target, is of the any type, and the second parameter is named methodName, and is of the string type. The third parameter is an optional parameter named descriptor, and is of the PropertyDescriptor type. This function can be used as a method decorator. 
  • The fourth function is named parameterDecorator, and also has three parameters. The first parameter is named target, and is of the any type. The second parameter is named methodName, and is of the string type. The third parameter is named parameterIndex, and is of the number type. This function can be used as a parameter decorator. 

Let’s now take a look at how we would use each of these decorators as follows: 

@classDecorator 

class ClassWithAllTypesOfDecorators { 

    @propertyDecorator 

    id: number = 1; 

    @methodDecorator 

    print() { } 

    setId(@parameterDecorator id: number) { } 

} 

Here, we have a class named ClassWithAllTypesOfDecorators. This class has an id property of the number type, a print method, and a setId method. The class itself has been decorated by our classDecorator, and the id property has been decorated by the propertyDecorator.

The print method has been decorated by the methodDecorator function, and the id parameter of the setId function has been decorated by the parameterDecorator. 

What is important to note about decorators is that it is the number of parameters and their types that distinguish whether they can be used as class, property, method, or parameter decorators. Again, the JavaScript runtime will fill in each of these parameters at runtime. 

Decorator factories 

On occasion, you will need to define a decorator that has parameters. In order to achieve this, we will need to use what is known as a decorator factory function. A decorator factory function is created by wrapping the decorator function itself within a function, as follows: 

function decoratorFactory(name: string) { 

    return (constructor: Function) => { 

        console.log(`decorator function called with : ${name}`); 

    } 

} 

Here, we have a function named decoratorFactory that accepts a single parameter named name of the string type. Within this function, we return an anonymous function that has a single parameter named constructor of the function type.

This anonymous function is our decorator function itself, and will be called by the JavaScript runtime with a single argument.

Within the decorator function, we are logging a message to the console that includes the name parameter passed in to the decoratorFactory function. You can now use this decorator factory as follows: 

@decoratorFactory(‘testName’) 

class ClassWithDecoratorFactory { 

} 

Here we have applied the decorator named decoratorFactory to a class named ClassWithDecoratorFactory, and supplied the string value of “testName” as the name argument. The output of this code is as follows: 

decorator function called with : testName 

Here, we can see that the anonymous function returned by the decoratorFactory function was invoked with the string “testName” as the value of the name argument. 

There are two things to note regarding decorator factory functions.

  1. Firstly, they must return a function that has the correct number of parameters, and types of parameters, depending on what type of decorator they are.
  2. Secondly, the parameters defined for the decorator factory function can be used anywhere within the function definition, which includes within the anonymous decorator function itself. 

This concludes our discussion of the setup and use of decorators.  

Summary 

In this article, we have explored the use of decorators within TypeScript. We started by setting up our environment to make use of decorators, and then discussed the syntax used to apply decorators.

You can apply decorators to classes, class properties, class methods, and even class parameters.

facebooktwitterlinkedinreddit
Share on:facebooktwitterlinkedinreddit

Tagged as:Frameworks

Kick Off a React JS Project: CRA, Next.js or Gatsby?
Previous Post
IBM Cloud CodeEngine: Beyond Serverless 1.0 for Large Organizations
Next Post

Related articles

  • Kick Off a React JS Project: CRA, Next.js or Gatsby?
  • Embedded Processing in Programmable Logic
  • Draw a Search: Implementing Polygon-based Searches on Maps
  • Why Should You Care to Learn TypeScript?

Primary Sidebar

About the author

Avatar
Nathan Rozentals
Nathan Rozentals has been writing commercial software for over 30 years, in C, C++, Java and C#. He…
Free Whitepaper: The Ultimate Azure Cybersecurity Handbook.

Codemotion Talent · Remote Jobs

Senior Backend Engineer

Unobravo
Full remote · TypeScript · Node.js · GraphQL · Amazon-Web-Services · Docker

Full Stack Senior .Net Developer

Lightcode
Full remote · .NET-Core · ASP.NET-MVC · C# · HTML/CSS · JavaScript

Full Stack Developer

Winelivery
Full remote · JavaScript · MySQL · Node.js · ReactJS · React-Native

Machine Learning Engineer

Tinexta
Full remote · Django · NLP · SQL · SQL-Server · Flask

Latest Articles

Infographic, visual studio code, project idx, codemotion, playlists, languages, tools for developers

Project Idx: A Visual Studio Code Competitor

Backend

angular, control flow

Angular Control Flow: the Complete Guide

Frontend

code mapping, code review best practices

Code Mapping: How It Works and 5 Compelling Use Cases

Backend

Conversative generative AI, compliance and privacy

How To Address Privacy, Compliance, and Fabrication Issues in Conversational Generative AI

AI/ML

Footer

Discover

  • Events
  • Community
  • Partners
  • Become a partner
  • Hackathons

Watch

  • Talks
  • Playlists
  • Edu paths

Magazine

  • Tech articles

Talent

  • Discover talent
  • Jobs

Companies

  • Discover companies

For Business

  • Codemotion for companies

About

  • About us
  • Become a contributor
  • Work with us
  • Contact us

Follow Us

© Copyright Codemotion srl Via Marsala, 29/H, 00185 Roma P.IVA 12392791005 | Privacy policy | Terms and conditions