I’m trying to make an @enumerable decorator that will expose properties defined via accessor methods.
A function to do this on instances of the class is fairly trivial:
// This works great when called in the class constructor like ```makeEnumerable(this, ['prop1', 'prop2'])```
const makeEnumerable = (what: any, props: string[]) => {
for (const property of props) {
const descriptor = Object.getOwnPropertyDescriptor(what.constructor.prototype, property);
if (descriptor) {
const modifiedDescriptor = Object.assign(descriptor, { enumerable: true });
Object.defineProperty(what, property, modifiedDescriptor);
}
}
};
However, it does not seem possible to turn this into a decorator, because it doesn’t have the instance.
// Does not work for Object.keys, Object.getOwnPropertyNames or Object.entries
function enumerable (value: boolean = true): any {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor): any {
if (descriptor) {
Object.assign(descriptor, { enumerable: value });
}
};
}
The property does still enumerate in for (const x in y) structures (strangely), but nowhere else – and worse, Object.entries throws an error.
Here is an example using the functions above:
class MyClass {
#privateVal1: any;
#privateVal2: any;
constructor () {
makeEnumerable(this, ['b']);
}
@enumerable(true)
get a () {
return this.#privateVal1;
}
set a (val: any) {
this.#privateVal1 = val;
}
get b () {
return this.#privateVal2;
}
set b (val: any) {
this.#privateVal2 = val;
}
}
const enumerableA = new MyClass();
enumerableA.a = 5;
enumerableA.b = 6;
const keys = [];
for (const key in enumerableA) {
keys.push(key);
}
console.log({
'forin': keys, // ['a', 'b']
'keys': Object.keys(enumerableA), // ['b']
'keys(proto)': Object.keys(Object.getPrototypeOf(enumerableA)), // ['a']
'getOwnPropertyNames': Object.getOwnPropertyNames(enumerableA), // ['b']
'getOwnPropertyNames(proto)': Object.getOwnPropertyNames(Object.getPrototypeOf(enumerableA)), // ['constructor', 'a', 'b']
});
console.log({
'entries': Object.entries(enumerableA), // Error('Cannot read private member #privateVal1 from an object whose class did not declare it');
'entries(proto)': Object.entries(Object.getPrototypeOf(enumerableA)), // Error('Cannot read private member #privateVal1 from an object whose class did not declare it');
});
Is there any way to use a decorator to make an accessor method an enumerable property?