Any ideas how I can use a decorator to convert a class field into a getter/setter? Example:
class Foo:
@accessor bar = 0;
const foo = new Foo;
Should exhibit custom behavior on say, foo.bar = 1;
I have already tried something like
function accessor(target, name, descriptor) {
let val;
return {
set: function(newVal) {
val = newVal;
console.log("setter called");
},
get: function() { return val; }
};
}
but this loses the initial value of bar = 0
.
The class requires to maintain private property where the value will be stored.
Since class fields aren't currently supported by decorators proposal and newer transform-decorators
Babel plugin, older transform-decorators-legacy
Babel plugin should be used instead.
As transform-decorators-legacy
documentation suggests, in order to provide get/set accessors for a property, initializer
method and writable
property should be deleted from descriptor object. Since initializer
function contains initial class field value, it should be retrieved and assigned to private property:
function accessor(classPrototype, prop, descriptor) {
if (descriptor.initializer)
classPrototype['_' + prop] = descriptor.initializer();
delete descriptor.writable;
delete descriptor.initializer;
descriptor.get = function () { return this['_' + prop] };
descriptor.set = function (val) { this['_' + prop] = val };
}
class Foo {
@accessor bar = 0;
}
const foo = new Foo ;
foo.bar = 1;
Due to the way how it works, initial value (0
) will be assigned to class prototype and won't trigger set
accessor, while next values (1
) will be assigned to class instance and will trigger set
accessor.
Since transform-decorators-legacy
isn't spec-compliant, this won't work other decorator implementations, e.g. TypeScript and decorator proposal.
A direct spec-compliant ES6 counterpart for the code above is:
class Foo {
get bar() { return this._bar };
set bar(val) { this._bar = val };
}
Foo.prototype._bar = 0;