如何在TypeScript中的枚举上使用装饰器

时间:2018-09-06 06:40:35

标签: typescript

enum Response {
    @Descriptor("this is No")
    No = 0,
    @Descriptor("this is Yes")
    Yes = 1,
}

如何在TypeScript中的枚举上使用装饰器,我尝试了这段代码,但是没有用

export function Description(description:string){
     return Reflect.metadata(descriptionMetadataKey, description);
}

4 个答案:

答案 0 :(得分:6)

简短的答案是,您不能(截至撰写本文时)。不过,还有其他选择。

替代:文档注释

如果您只想在枚举文字中添加说明,则可以使用文档注释。

enum Response {
    /**
     * this is No
     */
    No = 0,
    /**
     * this is Yes
     */
    Yes = 1,
}

虽然描述在运行时不可用,但它们会显示在编辑器自动完成中:

auto-completion example

替代:枚举类

如果您 really really 在运行时需要文字修饰的信息,则可以改用类。由于装饰器可以应用于类属性,因此您可以编写一个类,装饰其属性,然后使用该类的实例作为“枚举”。

function Descriptor(description: string) { 
    return (target: any, propertyName: string) => {
        // process metadata ...        
    };
}

class ResponsesEnum {
    @Descriptor("this is No")
    readonly Yes = 1;
    @Descriptor("this is No")
    readonly No = 2;
}
const Responses = new ResponsesEnum();

here试试。

答案 1 :(得分:1)

不能。 can在TypeScript中使用修饰符的位置:

类装饰器

@sealed
class Greeter {}

方法装饰器

class Greeter {
    @enumerable(false)
    greet() {
        return "Hello, " + this.greeting;
    }
}

访问装饰器

class Point {
    private _x: number;

    @configurable(false)
    get x() { return this._x; }
}

属性装饰器

class Greeter {
    @format("Hello, %s")
    greeting: string;
}

参数修饰符

class Greeter {
    greet(@required name: string) {
        return "Hello " + name + ", " + this.greeting;
    }
}

答案 2 :(得分:1)

另一种方法是将枚​​举值包装在类内部。然后,我们可以将装饰器应用于包装器类。这与this answer的“枚举类”替代方法不同,因为它保留了本机枚举。

优点是显而易见的。 G。更好的智能感知,类型安全性以及与期望本机枚举的API的兼容性。

一个缺点是用于声明和使用枚举的样板代码更多(例如,您必须编写new Response(value)来初始化包装的枚举)。

枚举声明:

enum ResponseEnum {
    No = 0,
    Yes = 1,
}

@EnumDescriptor( ResponseEnum, {
    No  = "this is No",
    Yes = "this is Yes",
    // Instead of strings, one could assign arbitrary metadata:
    // Yes = { answer: "this is Yes", more: 42 }
})
class Response {
    constructor( public value: ResponseEnum = ResponseEnum.No ){}
}

上面的代码是 typesafe ,这是通过装饰器功能的键入而实现的(请参见下文)。如果我们错过了描述符中的枚举键,或者传递给@EnumDescriptor的本机枚举类型与包装类使用的枚举键之间不匹配,则TS编译器将引发错误。

实例化枚举并获取元数据:

let r = new Response( ResponseEnum.Yes );

console.log("---VALUE---");
console.log( r );
console.log( +r );  // conversion to primitive, same as r.valueof()
console.log( r.valueOf() );
console.log( r.toString() );
console.log( JSON.stringify( r ) );

console.log("---METADATA---");

// Get metadata from variable
console.log( Reflect.getMetadata( EnumValuesMetadataKey, Object.getPrototypeOf( r ) ) );
console.log( Reflect.getMetadata( EnumDescriptorMetadataKey, Object.getPrototypeOf( r ) ) );

// Get metadata from the wrapper class:
console.log( Reflect.getMetadata( EnumValuesMetadataKey, Response.prototype ) );
console.log( Reflect.getMetadata( EnumDescriptorMetadataKey, Response.prototype ) ); 

控制台输出:

---VALUE---
Response {value: 1}
1
1
1
1
---METADATA---
{0: "No", 1: "Yes", No: 0, Yes: 1}
{No: "this is No", Yes: "this is Yes"}
{0: "No", 1: "Yes", No: 0, Yes: 1}
{No: "this is No", Yes: "this is Yes"}

修饰器功能(库代码)的实现:

尽管OP最初没有要求,但装饰器还通过EnumValuesMetadataKey(有关键值的映射)存储有关原始枚举类型的元数据。我们已经有了这些信息,对于像对象编辑器这样的用例来说,这很重要,我们想在运行时知道哪些枚举键可用于给定的枚举成员。

此外,Object.prototype的标准方法也被覆盖以“解包”本地枚举值,即转换为原始值(.valueOf()),字符串(.toString())和JSON( .toJSON())。这类似于Number这样的内置包装器类。

export interface IEnumWrapper< EnumT > {
    value: EnumT; 
}

type EnumWrapperCtor< EnumT > = new( value: EnumT ) => IEnumWrapper< EnumT >;

export const EnumValuesMetadataKey     = Symbol("EnumValuesMetadataKey");
export const EnumDescriptorMetadataKey = Symbol("EnumDescriptorMetadataKey");

export function EnumDescriptor< EnumT, KeysT extends string >(
    enumValues: { [ key in KeysT ]: EnumT },
    descriptor: { [ key in KeysT ]: any })
{
    return function( target: EnumWrapperCtor< EnumT > ) {
        // Assign metadata to prototype of wrapper class
        Reflect.defineMetadata( EnumValuesMetadataKey, enumValues, target.prototype );
        Reflect.defineMetadata( EnumDescriptorMetadataKey, descriptor, target.prototype);

        // Override standard methods to "unwrap" the enum value
        target.prototype.valueOf  = function() { return this.value };
        target.prototype.toJSON   = function() { return this.value; }
        target.prototype.toString = function() { return this.value.toString() };
    }
}

答案 3 :(得分:0)

装饰器对枚举无效。恐怕您不能以这种方式在枚举上使用装饰器。