Learn Javascript Class Decorators in 5 minutes
Tags: javascript, babel, node
Published: 2020-06-05 21:20:34

As of today, class decorators are not natively supported in the latest NodeJs environment - v14.3.0 ([status on node.green](https://node.green/#ESNEXT-draft--stage-2--Class-and-Property-Decorators-class-decorators)), which is why babel is required to use this feature.

To enable class decorators, babel plugin `@babel/plugin-proposal-decorators` can be setup like below:

```javascript
// .babelrc.json
{
  "presets": ["@babel/preset-env"],
  "plugins": [
    "@babel/plugin-proposal-decorators",
  ]
}
```

And make sure to add a build script to transpile all your ES6 Javascript source code (`src/`) to ES5 code (`dist/`) in your `package.json`:
```js
  "scripts": {
    "build": "babel src/**/*.js -d dist"
  }
```

A class constructor decorator is used like this:
```js
@Decorator
class MyClass {
}
```
As the name suggests, a constructor decorator allows to access the class being wrapped and the arguments received by its constructor. It is just a function which looks like below:
```js
function Decorator(target) { // target is basically "Class" in this case
  // do something with target...
};
```

A method decorator is used like so:
```js
class MyClass {
  @Decorator
  doSomething() {
    // do something amazing here..
  }
}
```
A class method decorator looks like this:
```js
function Decorator(target, key, descriptor) {
  // target refers to the Class
  // key is class method name
  // descriptor contains the object created by Object.defineProperty representing this method
}
```

Now, usage possibilities are endless, but as an example, let us create a decorator that logs the input arguments for a class constructor and class method.

```js
function logArgs(target, key, descriptor) {
  if (!key) { // it means it is applied on a class
    return WrapperClass extends target {
      constructor(...args) {
        console.log('constructor arguments :', args);
        super(...args);
      }
    }
  } else {
    // ...
  }
}
```
As you can see, this decorator, will check if it is applied to a class, return a wrapper class that extends the class being decorated and log its arguments. Simple!


```js
function logArgs(target, key, descriptor) {
  if (!key) { // it means it is applied on a class
    // ...
  } else {
    const originalMethod = descriptor.value;
    descriptor.value = function(...args) {
      console.log('method arguments :', args);
      const result = original.apply(this, args);
      return result;
    }
    return descriptor;
  }
}
```
Accessing args in class method decorator is a little trickier than class decorator. As mentioned above, descriptor is an object which represents this method. So, we can override its value, in order to access its args. And once that is done, return this updated descriptor.
In this override method, we call `.apply` function on original class to make sure that `this` is available to the class method and return the result.

That's all for now! :)
You just learned how to quickly get started and build awesome things with JS class decorators.

Hit the like button if you like this post & follow me if you'd like to read more short tutorials like this.