首页 文章

来自JSON字符串的Typescript`enum`

提问于
浏览
23

有没有办法让TypeScript枚举与JSON中的字符串兼容?

例如:

enum Type { NEW, OLD }

interface Thing { type: Type }

let thing:Thing = JSON.parse('{"type": "NEW"}');

alert(thing.type == Type.NEW); // false

我希望 thing.type == Type.NEW 是真的 . 或者更具体地说,我希望我可以将 enum 值指定为字符串,而不是数字 .

我知道我可以使用 thing.type.toString() == Type[Type.NEW] 但这很麻烦,似乎使枚举类型注释混乱和误导,这违背了它的目的 . 从技术上讲,JSON不提供有效的枚举值,因此我不应该将属性键入枚举 .

所以我现在正在做的是使用带有静态常量的字符串类型:

const Type = { NEW: "NEW", OLD: "OLD" }

interface Thing { type: string }

let thing:Thing = JSON.parse('{"type": "NEW"}');

alert(thing.type == Type.NEW); // true

这让我得到了我想要的用法,但是类型注释 string 过于宽泛且容易出错 .

我有点惊讶JavaScript的超集没有基于字符串的枚举 . 我错过了什么吗?有没有不同的方法可以做到这一点?


Update TS 1.8

使用string literal types是另一种选择(感谢@basaret),但要获得所需的类似枚举的用法(上面),它需要定义两次值:一次是字符串文字类型,一次是值(常量或命名空间):

type Type = "NEW" | "OLD";
const Type = {
    NEW: "NEW" as Type,
    OLD: "OLD" as Type
}

interface Thing { type: Type }

let thing:Thing = JSON.parse(`{"type": "NEW"}`);

alert(thing.type === Type.NEW); // true

这可行,但需要大量的样板,足够我不希望proposal for string enums最终将制定路线图 .


Update TS 2.1

新的keyof type lookup允许从const或命名空间的键生成字符串文字类型,这使得定义不那么冗余:

namespace Type {
    export const OLD = "OLD";
    export const NEW = "NEW";
}
type Type = keyof typeof Type;

interface Thing { type: Type }

const thing: Thing = JSON.parse('{"type": "NEW"}');
thing.type == Type.NEW // true

Update TS 2.4

TypeScript 2.4 added support for string enums!以上示例变为:

enum Type {
    OLD = "OLD",
    NEW = "NEW"
}

interface Thing { type: Type }
const thing: Thing = JSON.parse('{"type": "NEW"}');
alert(thing.type == Type.NEW) // true

这看起来近乎完美,但仍有一些心痛:

  • 你仍然需要写两次值,即 OLD = "OLD" ,并且's no validation that you don'有一个拼写错误,比如 NEW = "MEW" ...这已经在实际代码中咬了我一下 .

  • 有一些奇怪的东西(也许是错误?),如何对枚举进行类型检查,它不仅仅是一个字符串文字类型的简写,这才是真正正确的 . 我碰到的一些问题:

enum Color { RED = "RED", BLUE = "BLUE", GREEN = "GREEN" }

type ColorMap = { [P in Color]: number; }

declare const color: Color;
declare const map: ColorMap;
map[color] // Error: Element implicitly has an 'any' type because type 'ColorMap' has no index signature.

const red: Color = "RED"; // Type '"RED"' is not assignable to type 'Color'.
const blue: Color = "BLUE" as "RED" | "BLUE" | "GREEN"; // Error: Type '"RED" | "BLUE" | "GREEN"' is not assignable to type 'Color'.

用字符串文字类型替换 enum Color 的等效代码工作正常...

是的,我认为我对此有OCD,我只想要完美的JS枚举 . :)

4 回答

  • 0

    如果您在2.4版本之前使用Typescript,则有一种方法可以通过将枚举值转换为 any 来实现枚举 .

    example of your first implementation

    enum Type {
        NEW = <any>"NEW",
        OLD = <any>"OLD",
    }
    
    interface Thing { type: Type }
    
    let thing:Thing = JSON.parse('{"type": "NEW"}');
    
    alert(thing.type == Type.NEW); // true
    

    Typescript 2.4 has built in support for string enums已经,所以不再需要转换为 any ,你可以在不使用String Literal Union Type的情况下实现它,这对于验证和自动完成是可以的,但对于可读性和重构来说不太好,具体取决于使用场景 .

  • 0

    TS 2.9.2
    我的解决方案

    export enum Enums { VALUE1, VALUE2 }
    

    当我从API json获得 Value 时:

    switch (response.enumValue.toString()) { //can be without toString if we have string value from JSON.
        case Enums[Enums.VALUE1]:
          ...
        case Enums[Enums.VALUE2]:
          ...
     }
    
  • 10

    但类型注释字符串太宽泛且容易出错 .

    同意 . 一个快速的解决方法(如果您拥有丰富的代码生成,您可以自动执行此操作):

    interface Thing { type: "NEW" | "OLD" }
    

    这些在联合中称为字符串文字 . 更多:https://basarat.gitbooks.io/typescript/content/docs/tips/stringEnums.html

  • 1

    我一直在使用转换器功能作为权宜之计 . 希望这个线程达成一个决议:https://github.com/Microsoft/TypeScript/issues/1206

    enum ErrorCode {
        Foo,
        Bar
    }
    
    interface Error {
        code: ErrorCode;
        message?: string;
    }
    
    function convertToError(obj: any): Error {
        let typed: Error = obj as Error;
    
        // Fix any enums
        typed.code = ErrorCode[typed.code.toString()];
        return typed;
    }
    

相关问题