简体   繁体   English

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

[英]How to use decorators on enum in TypeScript

Like this像这样

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

How to use decorators on enum in TypeScript, I tried this code, but it didn't work如何在 TypeScript 中的枚举上使用装饰器,我试过这段代码,但没有用

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

Short answer is, you can't (as of this writing).简短的回答是,你不能(在撰写本文时)。 There are some alternatives though.不过也有一些替代方案。

Alternative: Doc Comments替代方案:文档注释

If you only want to add descriptions to your enum literals, you could use doc comments.如果您只想为枚举文字添加描述,则可以使用文档注释。

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

While the descriptions won't be available at runtime, they will show up in editor auto-completion:虽然描述在运行时不可用,但它们将显示在编辑器自动完成中:

自动完成示例

Alternative: Enum Class替代方案:枚举类

If you really , really need the decorated info on the literals at runtime, you could use a class instead.如果你真的真的需要在运行时文字上的装饰信息,你可以改用一个类。 Since decorators can be applied to class properties, you can write a class, decorate its properties and then use an instance of the class as your "enum".由于装饰器可以应用于类属性,因此您可以编写一个类,装饰其属性,然后使用该类的实例作为您的“枚举”。

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

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

Try it here . 在这里试试。

You can't.你不能。 Where you can use decorators in TypeScript:可以在 TypeScript 中使用装饰器的地方:

Class Decorators类装饰器

@sealed
class Greeter {}

Method Decorators方法装饰器

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

Accessor Decorators存取器装饰器

class Point {
    private _x: number;

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

Property Decorators物业装饰师

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

Parameter Decorators参数装饰器

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

Another approach is to wrap the enum value inside of a class .另一种方法是将枚举值包装在一个类中 Then we can apply the decorator to the wrapper class.然后我们可以将装饰器应用于包装类。 This is different from the "Enum Class" alternative of this answer , because it keeps the native enum.这与此答案的“枚举类”替代方案不同,因为它保留了本机枚举。

The advantages are obvious, eg better intellisense, type safety and compatibility with API that expect native enum.优点是显而易见的,例如更好的智能感知、类型安全以及与期望原生枚举的 API 的兼容性。

A disadvantage is more boilerplate code for declaring and using the enum (eg you have to write new Response(value) to initialize the wrapped enum).缺点是用于声明和使用枚举的样板代码较多(例如,您必须编写new Response(value)来初始化包装的枚举)。

Declaration of enum:枚举声明:

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 ){}
}

The above code is typesafe which is achived through the typing of the decorator function (see below).上面的代码是类型安全的,它是通过装饰器函数的类型实现的(见下文)。 If we miss an enum key in the descriptor or if there is a mismatch between the native enum type passed to @EnumDescriptor and the one used by the wrapper class, the TS compiler will raise an error.如果我们在描述符中遗漏了一个枚举键,或者如果传递给@EnumDescriptor的本机枚举类型与包装类使用的枚举类型不匹配,TS 编译器将引发错误。

Instanciate enum and get metadata:实例化枚举并获取元数据:

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 ) ); 

Console output:控制台输出:

---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"}

Implementation of decorator function (library code):装饰器函数的实现(库代码):

Though not originally requested by OP, the decorator also stores meta data about the original enum type via EnumValuesMetadataKey (mapping of keys to values).虽然最初不是由 OP 请求的,但装饰器还通过EnumValuesMetadataKey (键到值的映射)存储有关原始枚举类型的元数据。 We already have this information and it is very important for use cases like an object editor, where we want to know at runtime, which enum keys are available for a given enum member.我们已经有了这些信息,这对于像对象编辑器这样的用例非常重要,我们想在运行时知道哪些枚举键可用于给定的枚举成员。

Also, standard methods of Object.prototype are overridden to "unwrap" the native enum value, namely conversion to primitive value ( .valueOf() ), to string ( .toString() ) and to JSON ( .toJSON() ).此外, Object.prototype标准方法被覆盖以“解包”本机枚举值,即转换为原始值 ( .valueOf() )、字符串 ( .toString() ) 和 JSON ( .toJSON() )。 This is similar to what build-in wrapper classes like Number do.这类似于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() };
    }
}

Decorators are not valid on enums.装饰器对枚举无效。 You can't use decorators on enums in this fashion, I'm afraid.恐怕你不能以这种方式在枚举上使用装饰器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM