简体   繁体   English

来自 JSON 字符串的打字稿`enum`

[英]Typescript `enum` from JSON string

Is there any way to have a TypeScript enum compatible with strings from JSON?有没有办法让 TypeScript 枚举与来自 JSON 的字符串兼容?

For example:例如:

enum Type { NEW, OLD }

interface Thing { type: Type }

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

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

I would like thing.type == Type.NEW to be true.thing.type == Type.NEW是真实的。 Or more specifically, I wish I could specify the enum values to be defined as strings , not numbers.或者更具体地说,我希望我可以将enum值指定为字符串,而不是数字。

I am aware that I can use thing.type.toString() == Type[Type.NEW] but this is cumbersome and seems to make the enum type annotation confusing and misleading, which defeats its purpose.我知道我可以使用thing.type.toString() == Type[Type.NEW]但这很麻烦并且似乎使枚举类型注释混淆和误导,这违背了它的目的。 The JSON is technically not supplying a valid enum value, so I shouldn't type the property to the enum. JSON 在技术上没有提供有效的枚举值,所以我不应该在枚举中键入属性。

So what I am currently doing instead is using a string type with static constants:所以我目前正在做的是使用带有静态常量的字符串类型:

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

interface Thing { type: string }

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

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

This gets me the usage I want, but the type annotation string is way too broad and error prone.这让我得到了我想要的用法,但是类型注释string太宽泛且容易出错。

I'm a bit surprised that a superset of JavaScript doesn't have string based enums.我有点惊讶 JavaScript 的超集没有基于字符串的枚举。 Am I missing something?我错过了什么吗? Is there a different way this can be done?有没有不同的方法可以做到这一点?


Update TS 1.8更新 TS 1.8

Using string literal types is another alternative (thanks @basaret), but to get the desired enum-like usage (above) it requires defining your values twice : once in a string literal type, and once as a value (constant or namespace):使用字符串文字类型是另一种选择(感谢@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

This works but takes a lot of boilerplate, enough that I don't use it most of the time.这有效,但需要大量样板文件,足以让我大部分时间不使用它。 For now I'm hoping the proposal for string enums will eventually make the roadmap.现在我希望string enums提议最终会成为路线图。


Update TS 2.1更新 TS 2.1

The new keyof type lookup allows for the string literal type to be generated from the keys of a const or namespace, which makes the definition a little less redundant:新的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

Update TS 2.4更新 TS 2.4

TypeScript 2.4 added support for string enums ! TypeScript 2.4 添加了对字符串枚举的支持 The above example becomes:上面的例子变成:

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

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

This looks nearly perfect, but there's still some heartache:这看起来几乎完美,但仍有一些心痛:

  • You still have to write the value twice, ie OLD = "OLD" , and there's no validation that you don't have a typo, like NEW = "MEW" ... this has already bitten me in real code.仍然必须写两次值,即OLD = "OLD" ,并且没有验证你没有错字,比如NEW = "MEW" ......这已经在真正的代码中咬我了。
  • There's some oddities (perhaps bugs?) with how the enum is type checked, its not just a string literal type shorthand, which is what would be truly correct.枚举的类型检查方式有一些奇怪(也许是错误?),它不仅仅是字符串文字类型的速记,这才是真正正确的。 Some issues I've bumped into:我遇到的一些问题:

     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'.

    The equivalent code with enum Color replaced by string literal types work fine...用字符串文字类型替换enum Color的等效代码工作正常......

Yeah, I think I have OCD about this, I just want my perfect JS enums.是的,我想我对此有强迫症,我只想要我完美的 JS 枚举。 :) :)

If you are using Typescript before the 2.4 release, there is a way to achieve that with enums by casting the values of your enum to any .如果您在 2.4 版本之前使用 Typescript,则可以通过将 enum 的值转换为any来使用 enum 实现这一点。

An 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 already, so the cast to any would be no longer necessary and you could achieve it without the use of String Literal Union Type , which is ok for validation and autocomplete, but not so good for readability and refactoring, depending on the usage scenario. Typescript 2.4 已经内置了对字符串枚举的支持,因此不再需要转换为any并且您可以在不使用String Literal Union Type情况下实现它,这对于验证和自动完成来说是可以的,但对于可读性和重构,取决于使用场景。

TS 2.9.2 TS 2.9.2
My solution:我的解决方案:

export enum Enums { VALUE1, VALUE2 }

and when I have value from API json:当我从 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]:
      ...
 }

In case someone's still looking at this question in 2021:如果有人在 2021 年仍在关注这个问题:

@Aaron wrote in the original question: @Aaron 在原始问题中写道:

This looks nearly perfect, but there's still some heartache:这看起来几乎完美,但仍有一些心痛:

You still have to [...]你仍然必须[...]

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

The equivalent code with enum Color replaced by string literal types work fine...用字符串文字类型替换枚举颜色的等效代码工作正常......

Yeah, I think I have OCD about this, I just want my perfect JS enums.是的,我想我对此有强迫症,我只想要我完美的 JS 枚举。 :) :)

1. keyof typeof enumObj keyof typeof enumObj

Regarding,关于,

The equivalent code with enum Color replaced by string literal types work fine...用字符串文字类型替换枚举颜色的等效代码工作正常......

use the typeof and keyof operators in chained conjunction.在链式连接中使用typeofkeyof运算符。

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

No more implicit any when accessing map: ColorMap .访问map: ColorMap时不再隐含any
This will work with numeric enums as well (which can (and should more often than not) be const ).也适用于数字枚举(可以(并且通常应该)是const )。

From Typescript Handbook - Enums at compile time :来自Typescript Handbook - Enums at compile time

Even though Enums are real objects that exist at runtime, the keyof keyword works differently than you might expect for typical objects.即使枚举是在运行时存在的真实对象,keyof 关键字的工作方式与您对典型对象的预期不同。 Instead, use keyof typeof to get a Type that represents all Enum keys as strings.相反,使用 keyof typeof 获取将所有 Enum 键表示为字符串的 Type。

2. ts-enum-util 2. ts-enum-util

Check out ts-enum-util , which offers strongly typed interfaces to (likely) all your enum-related needs.查看ts-enum-util ,它为(可能)所有与枚举相关的需求提供强类型接口。

I've been using converter functions as a stopgap.我一直在使用转换器功能作为权宜之计。 Hopefully this thread comes to a resolution: https://github.com/Microsoft/TypeScript/issues/1206希望这个线程得到解决: 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;
}

but the type annotation string is way too broad and error prone.但是类型注释字符串太宽泛且容易出错。

Agreed.同意。 One quick workaround (if you have the luxury of code generation you can automate this):一种快速解决方法(如果您有足够的代码生成能力,您可以自动执行此操作):

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

These are called string literals in a union .这些在union中称为字符串文字 More : https://basarat.gitbooks.io/typescript/content/docs/tips/stringEnums.html更多: https : //basarat.gitbooks.io/typescript/content/docs/tips/stringEnums.html

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

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