【问题标题】:Typescript `enum` from JSON string来自 JSON 字符串的 Typescript `enum`
【发布时间】:2016-06-16 01:34:57
【问题描述】:

有没有办法让 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 的超集没有基于字符串的枚举。我错过了什么吗?有没有不同的方法可以做到这一点?


更新 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 最终会制定路线图。


更新 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

更新 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",并且没有验证您没有拼写错误,例如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 替换为字符串文字类型的等效代码可以正常工作...

是的,我想我对此有强迫症,我只想要完美的 JS 枚举。 :)

【问题讨论】:

    标签: json enums typescript


    【解决方案1】:

    但类型注释字符串过于宽泛且容易出错。

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

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

    这些在 union 中被称为 string literals。更多:https://basarat.gitbooks.io/typescript/content/docs/tips/stringEnums.html

    【讨论】:

    • 这看起来很有希望。我们将在升级到 TS 1.8 时进行调查。我正在玩它,但无法弄清楚如何将字符串文字值用作常量,例如 thing.type == Type.NEW.
    • This is as close as I could get,但它需要定义 Type 和值两次,一次作为 type 用作接口类型注释,再次作为 const 用于我需要价值观。这可以改进吗?
    【解决方案2】:

    我一直使用转换器功能作为权宜之计。希望这个帖子能解决: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;
    }
    

    【讨论】:

      【解决方案3】:

      如果您在 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 来实现它,这对于验证和自动完成来说是可以的,但对于可读性和重构来说不是那么好,视使用场景而定。

      【讨论】:

      • 谢谢!我希望我以前知道any 断言。现在,我正在尝试 TS 2.4 字符串枚举,它与我最初想要的非常接近……但我发现 TS 类型检查它的方式存在一些问题……
      • @Aaron 很酷,很高兴为您提供帮助!此外,您可能需要查看 ts-enums 项目,因为它使枚举处理非常通用且功能强大,适用于许多用例
      • 哇,这竟然是救命稻草!正如我在另一个答案中commented 一样,这仅在您的键与字符串值匹配时才有效,因为没有为字符串枚举生成反向映射。不管怎样,将字符串值转换为any 就足以让编译器创建反向映射。如果您可以将此信息编辑到您的答案中,那将非常有帮助。
      【解决方案4】:

      TS 2.9.2
      我的解决方案:

      export enum Enums { VALUE1, VALUE2 }
      

      当我从 API json 获得价值时:

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

      【讨论】:

      • 我在我的 OP 中提到了这一点,这不是我想要的。基本上,这意味着您的 JSON 满足有效的枚举值,并且您需要某种互操作代码(我猜您的示例显示的是什么)。我希望response.enumValue == Enums.VALUE1 是真实的并且类型兼容。
      【解决方案5】:

      如果有人在 2021 年仍在关注这个问题:

      @Aaron 在原问题中写道:

      这看起来几乎完美,但还是有些心痛:

      你仍然需要[...]

      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.
      // [...]
      

      将枚举颜色替换为字符串文字类型的等效代码工作正常...

      是的,我想我对此有强迫症,我只想要完美的 JS 枚举。 :)

      1。 keyof typeof enumObj

      关于,

      将枚举颜色替换为字符串文字类型的等效代码工作正常...

      typeofkeyof 运算符串联使用。

      type ColorKeys = keyof typeof Color
      type ColorMap = { [P in ColorKeys]: number; } // will have strongly typed keys
      

      访问map: ColorMap 时不再隐含any
      也适用于数字枚举(可以(并且通常应该)是const)。

      来自Typescript Handbook - Enums at compile time

      尽管枚举是在运行时存在的真实对象,keyof 关键字的工作方式与您对典型对象的预期不同。相反,使用 keyof typeof 来获取将所有 Enum 键表示为字符串的 Type。

      2。 ts-enum-util

      查看ts-enum-util,它为(可能)所有与枚举相关的需求提供了强类型接口。

      【讨论】:

        猜你喜欢
        • 2022-12-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-13
        • 2017-03-03
        相关资源
        最近更新 更多