If I flag a property as @enumerable(false)
using TypeScript and the method below, the child classes that extend the parent class where this enumerable is flagged to false will have the property but it will be enumerable taking from this answer.
export {}
declare global {
function enumerable(value: boolean): any;
}
const _global = (global /* node */ || window /* browser */) as any;
/**
* @enumerable decorator that sets the enumerable property of a class field to false.
* @param value true|false
*/
_global.enumerable = function(value: boolean): any {
return function (target: any, propertyKey: string) {
let descriptor = Object.getOwnPropertyDescriptor(target, propertyKey) || {};
if (descriptor.enumerable != value) {
descriptor.configurable = true;
descriptor.writable = true;
descriptor.enumerable = value;
Object.defineProperty(target, propertyKey, descriptor)
}
};
}
Here's what my hierarchy looks like:
class BaseObject {
@enumerable(false)
public _metadata: any = {
id: 'number',
name: 'string'
};
}
class ContainerObject extends BaseObject {
// ...
}
class CanvasObject extends BaseObject {
// ...
}
And here's what the value of the descriptor is at runtime:
var canvas = new CanvasObject();
console.log('Metadata Descriptor: ');
console.log(Object.getOwnPropertyDescriptor(canvas, '_metadata'));
Metadata Descriptor:
{ value:
{ beanId: 'number',
name: 'string' },
writable: true,
enumerable: true,
configurable: true }
How can I ensure that this property is enumerable: false
in the parent class and all subsequent inherited classes?
This is caused by the fact that a decorator is applied to class prototype on class declaration. Since _metadata
is instance property (it's desugared to constructor() { this._metadata = ... }
), enumerable
decorator doesn't affect it.
Here is an example of enumerable
decorator that can be applied to both prototype (usually methods) and instance properties:
function enumerable(value: boolean) {
return (target: any, prop: string, descriptor?: PropertyDescriptor) => {
if (descriptor) {
// prototype property
descriptor.enumerable = value;
} else {
// instance property
let propSymbol = Symbol(prop);
Object.defineProperty(target, prop, {
configurable: true,
enumerable: value,
get() { return this[propSymbol] },
set(value) { this[propSymbol] = value }
});
}
};
}
Notice that in order to deal with _metadata = ...
property initializer, descriptor should contain set
accessor to catch property assignments.