DEV Community

Cover image for Using Class Decorators in Typescript with a real example
Dany Paredes
Dany Paredes

Posted on • Edited on • Originally published at danywalls.com

Using Class Decorators in Typescript with a real example

The Decorators are a great Typescript feature, maybe you see them all over Angular and other frameworks.

It helps to write code clean and declarative, maybe you already use every day, but do you know when to create your own decorators?

I will show how to create your own decorators and even how to implement it.

What is a Decorator?

The decorator is a function that we can hook into our code, to extend with some behavior and helps us to write code abstractions that help extend code clear.

The decorator function depends on which you will decorate, that's mean it doesn't get the same parameters when it is working over a class, methods or properties.

Where can I Use Decorators?

The decorators are used in some class code areas.

  • class definition
  • properties
  • methods
  • accessors
  • parameters.

My first class decorator

The decorators in classes take the constructor function as a parameter that means we can change the way how its class is initialized.

Let me show a simple case, we have a few entities class, everyone needs to have the id and created property, the normal solution is to create a base class.

class BaseEntity {
  readonly id: number;
  readonly created: string;
  constructor() {
    this.id = Math.random();
    this.created = new Date().toLocaleDateString();
  }
}
Enter fullscreen mode Exit fullscreen mode

The Course class extends from BaseEntity class and call the constructor super().

class Course extends BaseEntity {
  constructor(public name: string) {
    super();
  }
}

let englishCourse = new Course("English");
console.log("id: " + englishCourse.id);
console.log("created: " + englishCourse.created);
Enter fullscreen mode Exit fullscreen mode

Perfect, it works. But How can we solve it with decorators?

The decorator

The decorator class is a function and gets the constructor as a parameter, then include the id and created properties.

function BaseEntity(ctr: Function) {
  ctr.prototype.id = Math.random();
  ctr.prototype.created = new Date().toLocaleString("es-ES");
}
Enter fullscreen mode Exit fullscreen mode

The decorator is ready to be used in each entity without modifying his constructor or extend, only needs to add @Entity before class definition.

@BaseEntity
class User {
  constructor(public name: string) {}
}

@BaseEntity
class City {
  constructor(public zicode: number) {}
}

let user = new User("dany");
let ny = new City("RD");
//City and User classes has the id and created property ;)
console.log(ny.id);
console.log(user.id);
Enter fullscreen mode Exit fullscreen mode

Decorator Factory

The decorator factory is a function that returns the decorator function itself, it gives the flexibility to pass parameters to the decorators.

A new property position is a number a random number between 0 to some parameter number.

function LuckyNumber(limit: number) { 
  return function (constructor: Function) { 
    constructor.prototype.lucky = Math.floor(
      Math.random() * Math.floor(limit)
  }
}
Enter fullscreen mode Exit fullscreen mode

The Decorator LuckyNumber get a number as parameter to set the range limit, the new Decorator can be use nested with other.

@BaseEntity
@LuckyNumber(3)
class User {
  constructor(public name: string) {}
}

@BaseEntity
@LuckyNumber(3)
class City {
  constructor(public zicode: number) {}
}

let user = new User("dany");
let ny = new City(08930);
//the City and User has the lucky number property
console.log(ny.lucky);
console.log(user.lucky);
Enter fullscreen mode Exit fullscreen mode

The decorators help to add behavior and metadata to classes and also methods or properties using a declarative way.

That's it!

Hopefully, that will give you a bit of help with how and when using Decorators in class with Typescript. If you enjoyed this post, share it!

Photo by Ferenc Almasi on Unsplash

Top comments (2)

Collapse
 
xirurgx3d profile image
Алексей

так ты гонишь без определение типа в классе никаого "ny.lucky" не получится, тебе выдаст ошибку lucky нету в данном классе...

Collapse
 
vilgeforc5 profile image
Boris

а как можно без as типизировать?
ну вот как в более продвинутых случаях, если мы, например, возвращаем из декоратора return class extends constructor {....}?