来自 JSON 字符串的 Typescript 枚举

问题描述 投票:0回答:5

有没有办法让 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
值指定为 strings,而不是数字。

我知道我可以使用

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

使用字符串文字类型是另一种选择(感谢@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

这可行,但需要大量样板,足以让我大部分时间不使用它。现在我希望 string enums

提案最终能够成为路线图。


更新TS 2.1

新的

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

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 添加了对字符串枚举的支持!上面的例子就变成:

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
5个回答
34
投票

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

any
来实现这一点。

首次实施的示例

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 已经内置了对字符串枚举的支持,因此不再需要转换为

any
,并且您可以在不使用
String Literal Union Type
的情况下实现它,这对于验证和自动完成来说是可以的,但是可读性和重构不太好,具体取决于使用场景。


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

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

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

1.
keyof typeof enumObj

关于,

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

串联使用

typeof
keyof
运算符。

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

访问

any
时不再隐式
map: ColorMap

也适用于数字枚举(可以(并且应该更经常)是
const
)。

来自Typescript Handbook - 编译时的枚举

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

2.
ts-enum-util

查看

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


3
投票

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]:
      ...
 }

1
投票

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

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

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

这些在union中称为字符串文字。更多: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;
}
© www.soinside.com 2019 - 2024. All rights reserved.