DEV Community

Deepak Sharma
Deepak Sharma

Posted on • Edited on

Learn Javascript Class Decorators in 5 minutes

As of today, class decorators are not natively supported in the latest NodeJs environment - v14.3.0 (status on node.green), 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:

// .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:

  "scripts": {
    "build": "babel src/**/*.js -d dist"
  }

A class constructor decorator is used like this:

@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:

function Decorator(target) { // target is basically "Class" in this case
  // do something with target...
};

A method decorator is used like so:

class MyClass {
  @Decorator
  doSomething() {
    // do something amazing here..
  }
}

A class method decorator looks like this:

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.

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!

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.

Top comments (1)

Collapse
 
xgqfrms profile image
xgqfrms

pseudo code bug

missing the class keyword

  if (!key) {
    // ❌ return WrapperClass extends target {
    return class WrapperClass extends target {
      constructor(...args) {
        console.log('constructor arguments :', args);
        super(...args);
      }
    }
  } 

Enter fullscreen mode Exit fullscreen mode