Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a TypeScript @enumerable(false) decorator for a property

I want to create a decorator in TypeScript in order to be able to make a class property not enumerable.

I found an example of @enumerable here: https://www.typescriptlang.org/docs/handbook/decorators.html#method-decorators but that only seems to work for methods, not properties:

https://www.typescriptlang.org/docs/handbook/decorators.html#property-decorators

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. As such, a property decorator can only be used to observe that a property of a specific name has been declared for a class.

Is there a way to create a @enumerable decorator for a class property?

Thanks

like image 794
mvermand Avatar asked Dec 02 '16 10:12

mvermand


2 Answers

I ended up with this solution:

/**
 * @enumerable decorator that sets the enumerable property of a class field to false.
 * @param value true|false
 */
function enumerable(value: boolean) {
    return function (target: any, propertyKey: string) {
        let descriptor = Object.getOwnPropertyDescriptor(target, propertyKey) || {};
        if (descriptor.enumerable != value) {
            descriptor.enumerable = value;
            descriptor.writable= true;
            Object.defineProperty(target, propertyKey, descriptor)
        }
    };
}

Usage:

class User {
    id:string;

    @enumerable(false)
    name: string;
}

Testing:

   var user = new User();
   user.id = 1;
   user.name = 'John Doe';
   for (key in user){ console.log(key, user[key]);}

Output

id 1

Same test without the use of the decorator

id 1
name John Doe
like image 124
mvermand Avatar answered Oct 03 '22 21:10

mvermand


This solution doesn't actually work, or doesn't work with modern typescript. The following does however:

const enumerable: {
    (target: any, name: string): void;
    (target: any, name: string, desc: PropertyDescriptor): PropertyDescriptor;
} = (target: any, name: string, desc?: any) => {
    if(desc) {
        desc.enumerable = true;
        return desc;
    }
    Object.defineProperty(target, name,  {
        set(value) {
            Object.defineProperty(this, name, {
                value, enumerable: true, writable: true, configurable: true,
            });
        },
        enumerable: true,
        configurable: true,
    });
};

const nonenumerable: {
    (target: any, name: string): void;
    (target: any, name: string, desc: PropertyDescriptor): PropertyDescriptor;
} = (target: any, name: string, desc?: any) => {
    if(desc) {
        desc.enumerable = false;
        return desc;
    }
    Object.defineProperty(target, name,  {
        set(value) {
            Object.defineProperty(this, name, {
                value, writable: true, configurable: true,
            });
        },
        configurable: true,
    });
};
like image 32
JohnLock Avatar answered Oct 03 '22 21:10

JohnLock