I am trying to understand how to use decorators in a very simple piece of code, so I can apply this concept to my bigger project. Taking cue from Addy Osmani's article here, I created a simple piece of code as below.
Say, I have a class called Cat
, with a meow()
method, I want to decorate it with some logging, as below.
class Cat {
@logger
meow() { console.log( ' Meeeoow! ') }
};
function logger(target, key, descriptor) {
console.log("Cat snarling...");
return descriptor;
}
const cat = new Cat();
cat.meow();
When I try to execute this against the Node.js interpreter (version 9.1.0), I get the following error.
/Users/ravindranath/projects/decorators/index.js:2 @logger ^ SyntaxError: Invalid or unexpected token at createScript (vm.js:80:10) at Object.runInThisContext (vm.js:152:10) at Module._compile (module.js:605:28) at Object.Module._extensions..js (module.js:652:10) at Module.load (module.js:560:32) at tryModuleLoad (module.js:503:12) at Function.Module._load (module.js:495:3) at Function.Module.runMain (module.js:682:10) at startup (bootstrap_node.js:191:16) at bootstrap_node.js:613:3
So, my questions are:
Does Node.js 9.x support decorator syntax? Or is it coming up in some future version?
I see some express-js
based decorators on GitHub, but I am unable to figure out how to create my own decorator. Can someone provide a simple basic example of creating a custom decorator with Node.js?
Decorators are not part of ECMAScript 2016 (aka 7). Decorators are currently in Stage 2 Draft out of the total 4 stages a feature goes through before being finalized and becoming part of the language. They'll probably be integrated into the language in the near future, but its features and specifics are subject to change. Because of this, you'll have to use a transpiler such as Babel to transform the decorators into code the Node runtime can understand (ECMAScript 2016) by installing the transform-decorators
Babel plugin.
As for creating decorators, you've already done so. Each decorator is just a function that wraps another, that is provided with arguments based on the usecase, in your case target
, key
, and descriptor
. Your logger
function:
function logger(target, key, descriptor) {
console.log("Cat snarling...");
return descriptor;
}
Is already a decorator. For class properties and methods, target
refers to the class of the property, key
is the property name, and descriptor
is the descriptor of the property. The decorator is then called and the property of the class is defined via Object.defineProperty
once desugared. Your example can be boiled down to this:
class Cat { }
let meowDescriptor = {
type: 'method',
initializer: () => () => {
console.log(' Meeeoow! ');
},
enumerable: false,
configurable: true,
writable: true
}
function logger(target, key, descriptor) {
console.log("Cat snarling...");
return descriptor;
}
meowDescriptor = logger(Cat.prototype, 'meow', meowDescriptor);
Object.defineProperty(Cat.prototype, 'meow', {
...meowDescriptor,
value: meowDescriptor.initializer()
});
For classes themselves, decorators take one argument, target
which describes the decorated class. I suggest reading some documentation on the subject to become acquainted with it.