javascript-decorators
javascript-decorators copied to clipboard
Change a property to getter/setter with decorator?
Is it possible that we can change a property to a getter / setter in decorator's descriptor.initializer
function?
class CustomView {
constructor() {
this.controls = { table: 'table' };
}
@bind('table')
table
}
function bind(name) {
return (target, key, descriptor) => {
descriptor.initializer = () => {
Object.defineProperty(target, key, {
get: function () { return this.controls[key]; }
});
};
};
}
I suggest that we have a way to do that, eg. the initializer
return a special value
Here's an idea:
class CustomView {
constructor() {
this.controls = { table: 'table' };
}
@bind table() {}
}
function bind(name) {
return (_, key, {configurable, enumerable, writable}) => {
return {
configurable, enumerable, writable,
get() { return this.controls[key]; },
set(val) { return (this.controls[key] = val); },
};
};
}
Should that work?
I figured out the same issue. My plan was to have an decorator which define a custom setter and getter on the decorated property. That works for me but the decorated properties aren't anymore in serialization process included. I tried it with defineProperty, returning a new descriptor object and modifying the old descriptor object.
function decorator(target, property, descriptor) {
let val;
return {
set: function (value) {
val = value;
console.log('set', value);
},
get: function() {
return val;
console.log('get', val);
},
enumerable: true,
configurable: true
};
};
class Test {
id = 0;
@decorator
test = 'something';
}
var t = new Test();
console.log(JSON.stringify(t));
t.test = 123;
console.log(JSON.stringify(t));
console.log(JSON.stringify(t.test));
The log will print
{"id":0}
set 123
{"id":0}
123
As you might see there is no test property included but accessing the test property shows it's there. This might happen because t.hasOwnProperty('test') returns false. Any idea how to fix it or how to work around it?
@fragsalat Add a toJSON
method returning the relevant keys. That should fix your problem. It's technically there, just JSON.stringify won't touch the prototype other than to check for a toJSON
method.
@fragsalat I tested your code and verified that it works. I don't know how to reconcile it though with the official docs on property decorators.
from https://www.typescriptlang.org/docs/handbook/decorators.html#property-decorators
Property Decorators
A Property Decorator is declared just before a property declaration. A property decorator cannot be used in a declaration file, or in any other ambient context (such as in a declare class).
The expression for the property decorator will be called as a function at runtime, with the following two arguments:
Either the constructor function of the class for a static member, or the prototype of the class for an instance member. The name of the member. NOTE A Property Descriptor is not provided as an argument to a property decorator due to how property decorators are initialized in TypeScript. This is because there is currently no mechanism to describe an instance property when defining members of a prototype, and no way to observe or modify the initializer for a property. The return value is ignored too. As such, a property decorator can only be used to observe that a property of a specific name has been declared for a class.
Maybe use a private __propName
key
function decorator(target, property, descriptor) {
let val;
return {
set: function (value) {
this['__'+property] = value;
console.log('set', value);
},
get: function() {
console.log('get', val);
return this['__'+property];
},
enumerable: true,
configurable: true
};
};
class Test {
id = 0;
@decorator
test = 'something';
}
var t = new Test();
console.log(JSON.stringify(t));
t.test = '111';
console.log(JSON.stringify(t));
console.log(JSON.stringify(t.test));
@rgigger I think the confusion is that you linked to TypeScript docs, which had decorators pre-ESnext, and in their impl, they don't turn properties into Object.defineProperty, hence there is no descriptor for the decorator to mutate.
However, if you look at the latest ESnext docs, their first example is a regular property being decorated and provided the property descriptor:
https://github.com/tc39/proposal-decorators/blob/master/METAPROGRAMMING.md
(Note this entire wycats repo has been superseded by that tc39 repo.)
E.g. I'm trying to use @fragsalat's code in TypeScript, and it does not work, but I imagine you've both seen it work in ES6/ESnext.
I'm poked around for an issue for bringing TypeScript property decorators up to match ESnext, but haven't found it yet.