Start Implementing your own Typescript Class Decorators
Tags: classdecorator, decorators, typescript, node
Published: 2023-09-12 11:57:45

---
title: Start Implementing your own Typescript Class Decorators
published: true
date: 2021-12-23 12:53:42 UTC
tags: classdecorator,decorators,typescript,nodejs
canonical_url: https://levelup.gitconnected.com/start-implementing-your-own-typescript-class-decorators-84a49f560dea
---

### Start Implementing Your Own Typescript Class Decorators

![](https://cdn-images-1.medium.com/max/700/1*2alD5jlp7tfo9aAtTYqPAQ.png)
_Class Decorator_

#### What is a Decorator?

It is a structural design pattern that lets you attach new behaviors to objects by placing these objects inside special wrapper objects that contain the behaviors ([reference](https://refactoring.guru/design-patterns/decorator)).

**Typescript Class Decorators Definition:** _The class decorator is applied to the constructor of the class and can be used to observe, modify, or replace a class definition.(_[_reference_](https://www.typescriptlang.org/docs/handbook/decorators.html#class-decorators)_)_

#### What is going on behind the scene?

Your class decorator is actually **a simple function** that is called as a function **at runtime** and it gets _one argument_ and that is **the constructor function of the class**. If _the class decorator_ returns something it is going to be used as the constructor of the class. These things might sound confusing but it’s going to be clear as day after taking a look at some pieces of code.

### Setup

In order to run the Typescript code, we need to compile them using the Typescript compiler.

We need a tsconfig.json file :

<iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/39e61b1249de30de02c2830a5244d69b/href">https://medium.com/media/39e61b1249de30de02c2830a5244d69b/href</a></iframe>

We have to enable the experimentalDecorators. Also, the target should not be less than ES5.

If you do not want to use a tsconfig file you can pass these options directly:

```
tsc --experimentalDecorators // If you installed tsc globaly
npx tsc --experimentalDecorators // If you installed tsc in your current directory
```

Now by running tsc in the current directory, the typescript files will compile to Javascript files and we can run them using Node.([reference](https://dev.to/pshaddel/start-writing-your-own-typescript-method-decorators-3jm4-temp-slug-1785958))

### Define a Class Decorator

First class decorator we want to create is just going to log a message on the console and tell us that it is running.

<iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/86a7aa80e3ee4ce7a713f02af9a16a81/href">https://medium.com/media/86a7aa80e3ee4ce7a713f02af9a16a81/href</a></iframe>

Now if I compile and run this code I will see this on the console:

![](https://cdn-images-1.medium.com/max/854/1*s1U7_vZJObXq44qr0vnkKw.png)
_Result of running the code_

Here the class decorator was a function that had _constructor_ of the class as its argument and logs something on console easy-peasy. We can see that **before** running the constructor of the class it runs _the class decorator_.

Let’s see an example that we can pass our constructor in the decorator:

<iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/56031b8ba6d683b3215b01a8a67006d5/href">https://medium.com/media/56031b8ba6d683b3215b01a8a67006d5/href</a></iframe>

In order to make some changes to constructor of the class from _the class decorator_ we have to return a class that **extends** the **base class**.

The Typescript compiler forces us to do this(return a class that extends the base class).

**Why compiler is forcing us do this?** Because otherwise the type we are returning is not compatible with the base class.

If what is happening is still vague you should recall that classes are actually functions(You may find so many helpful comments on that [here](https://stackoverflow.com/questions/11970141/javascript-whats-the-difference-between-a-function-and-a-class)).

<iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8149596a93aed43fd64e86da5feb9660/href">https://medium.com/media/8149596a93aed43fd64e86da5feb9660/href</a></iframe>

Now that we know this, consider the class decorator as a higher order function. It is somehow similar to the implementation of [method decorator](https://dev.to/pshaddel/start-writing-your-own-typescript-method-decorators-3jm4-temp-slug-1785958).

**What if we want to use a class decorator on all classes?** That is possible by using Typescript Generics. Take a look at this example:

<iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1bca8141f9a41e450fe6431a1746c53f/href">https://medium.com/media/1bca8141f9a41e450fe6431a1746c53f/href</a></iframe>

We can use this class decorator on all classes.

```
<T extends { new (...args: any[]): {} }>
```

This is the definition of generic and if you are wondering why it extends { new (...args: any[]): {} you should know that we expect a constructor function and this is how we can tell the compiler to check that.

Here T is the generic class and we are able to return new constructor by extending it in line 4.

In line 5 and 6 we added two timestamps members to the class. Now each class that wrapped by this decorator has these two timestamps created\_at and updated\_at .

Let’s see the result on console:

![](https://cdn-images-1.medium.com/max/849/1*SP0IKIunc_BSoeexalIqvg.png)
_Timestamp Decorator_

As we can see class has two additional members updated\_at and created\_at

**Can we access these two additional properties?**

![](https://cdn-images-1.medium.com/max/1021/1*Jg09ueD7SBMZkSyEbYuQfg.png)
_Calling Additional Properties that added by Decorator_

> _The Decorator does not change the Typescript Type_

The only way to access that property is to tell the compiler to ignore that line. If you consider timestamps example, we usually want to pass them to an ORM or ODM to save a record or document in the database, so there should be no problem even if we do not call them directly. These properties are going to be passed in the object.

Another really useful decorator that is mentioned in official TS Docs is **sealed decorator**.

### Sealed Decorator

When we seal a class we want to prevent more functionalities and properties to be added or removed. Let’s see that in action:

<iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/615cd03dd8d6e9f73a2173da533cf301/href">https://medium.com/media/615cd03dd8d6e9f73a2173da533cf301/href</a></iframe>

We sealed the constructor and it’s prototype. If we try to change the constructor this way(We had to ignore the compiler to be able to do this):

<iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5f85915def8a4c38978cfd6a3cb7c8e4/href">https://medium.com/media/5f85915def8a4c38978cfd6a3cb7c8e4/href</a></iframe>

That will result in a TypeError:

![](https://cdn-images-1.medium.com/max/692/1*Wp71zaQwZhHLN5xH7GEKAQ.png)
_Result of changing a class prototype_

If we do not use @sealed decorator then this is going to be the result:

![](https://cdn-images-1.medium.com/max/692/1*W1rsl1_0TqEAAgXj2cDpNA.png)
_Calling getHeight without sealed decorator_

### Conclusion

Class decorators are simply a function that runs at runtime and it can change behavior of a class. Using Class Decorators and wrapping other classes can be a little bit tricky in some cases if you want to add some functionalities to them since the decorator does not change the typescript type.

Decorators are widely used in [Angular](https://angular.io) and [NestJS](https://docs.nestjs.com/) since both of these frameworks use OOP paradigm and you can take a look at their documents and the implementation of decorators.

* * *