来自JSON字符串的Typescript`enum`

Aar*_*all 32 enums json typescript

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

例如:

enum Type { NEW, OLD }

interface Thing { type: Type }

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

alert(thing.type == Type.NEW); // false
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

这让我得到了我想要的用法,但是类型注释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
Run Code Online (Sandbox Code Playgroud)

这可以工作,但需要大量的样板,足以让我大部分时间不使用它.目前我希望这个提案string enums能够最终制定路线图.


更新TS 2.1

keyof类型查找允许从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
Run Code Online (Sandbox Code Playgroud)

更新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
Run Code Online (Sandbox Code Playgroud)

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

  • 仍然必须写两次值,即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'.
    
    Run Code Online (Sandbox Code Playgroud)

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

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

Fel*_*ino 12

如果您在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
Run Code Online (Sandbox Code Playgroud)

Typescript 2.4已经内置了对字符串枚举的支持,因此any不再需要强制转换,你可以在不使用的情况下实现它String Literal Union Type,这对于验证和自动完成是可以的,但对于可读性和重构来说不是很好,这取决于使用场景.

  • 哇,原来是救生员!正如我对另一个答案的评论(/ q / 17380845 /如何将字符串转换为枚举类型#comment94130934_52933064),这仅在您的键匹配字符串值时才有效,因为不会为字符串枚举。无论如何,将字符串值强制转换为“ any”足以使编译器创建反向映射。如果您可以将此信息编辑成答案,那将非常有帮助。 (2认同)

Joh*_*lle 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.
// [...]
Run Code Online (Sandbox Code Playgroud)

用字符串文字类型替换枚举 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
Run Code Online (Sandbox Code Playgroud)

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

来自Typescript 手册 - 编译时的枚举

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

2.ts-enum-util

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