February 23, 2023

TypeScript - Class Decorators

The class decorator takes the constructor function as a parameter, allows us to change the way how this class is initialized.

Let's say we have a couple of classes Customer and Order. It is required that every class needs to have created property.

The normal solution is to create a base class which will have common fields and allow the childern to inherit from this.

In this example, we will use decorator to achieve this behavior, the decorator function will receive the target's constructor function as a parameter, and add the created property to its prototype.

Here is the code for decorator:

function EntityWithTimeStamp(constructorFunction: Function) {
    constructorFunction.prototype.created = new Date().toLocaleString("en-US");
    console.log("decorator called");
}

It receives constructorFunction(of the target class) as parameter, and adds created property to its prototype.

The decorator is ready to be used in each entity. We only need to add @EntityWithTimeStamp before class definition.

Here we define our classes and adds @EntityWithTimeStamp decorator.

@EntityWithTimeStamp
class Customer {
  constructor(public name: string) {
    console.log("Customer controctor called");
  }
}

@EntityWithTimeStamp
class Order {
  constructor(public amount: number) {
    console.log("Order  controctor called");
  }
}

Both of these classes have defined their own public properties received in constructor. Also they will have an additional property created which will be added by the decorator.

You can create objects of these classes as usual, and output the public properties to console:

let customer = new Customer("Idrees");
let order  = new Order(100);

console.log(order.amount);
console.log(customer.name);

Note that the decorator does not change the TypeScript type and the new property created is not known to the type system. The following lines will give you the error because compiler will not find created property in these referenced classes/types:

console.log(order.created);
console.log(customer.created);

Error message:

error TS2339: Property 'created' does not exist on type 'Customer'.
error TS2339: Property 'created' does not exist on type 'Order'.

There is a work around this issue, you can access the created property by temporarily casting the object to type any.

console.log((<any>order).created);
console.log((<any>customer).created);

Output will display the value of crearted property, which shows that decorator has added this field as the class member.

Related Post(s):

February 22, 2023

Decorators in TypeScript

Decorators provide a mechanism to metaprogramming syntax in TypeScript, which is a programming technique basically means "writing code that writes code".

Decorators allow us to decorate members of a class, or a class itself, with extended functionality. This is a function that we can hook into our code, to extend with some behavior and helps us to write code abstractions and provide extension mechanism.

When you apply a decorator to a class or a class member, it will actually call a function that is going to receive details of target (what is being decorated), and the decorator implementation will then be able to transform the code dynamically (e.g. adding extra functionality, and reducing boilerplate code).

The decorators are used on class itself and its members:

  • class definition
  • properties
  • methods
  • accessors
  • parameters

Note that:

Decorators are a stage 2 proposal for JavaScript and are available as an experimental feature of TypeScript.

Before using decorators in TypeScript, we need to enable it. If the decorator support is not enabled and you try to compile a TypeScript file/project that is using decorator, it will give you this error:

 error TS1219: Experimental support for decorators is a feature that is subject to change 
 in a future release. Set the 'experimentalDecorators' option in your 'tsconfig' or 'jsconfig'
 to remove this warning.

We have two ways to enable decorators support in TypeScript:

  • You can enable decorators support at compile time. When using the TypeScript Compiler CLI (tsc), we need to provide additional flag --experimentalDecorators:
    tsc --experimentalDecorators
    
  • When working in a project that has a tsconfig.json file, to enable decorators support you can add the experimentalDecorators property (with value true) to the compilerOptions object:
    {
      "compilerOptions": {
        "experimentalDecorators": true
      }
    }
    

Related Post(s):