来自 JSON 字符串的打字稿`enum`
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/35760096/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Typescript `enum` from JSON string
提问by Aaron Beall
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 likething.type == Type.NEWto be true. Or more specifically, I wish I could specify the enumvalues to be defined as strings, not numbers.
我想thing.type == Type.NEW是真的。或者更具体地说,我希望我可以指定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. The JSON is technically notsupplying a valid enum value, so I shouldn't type the property to the enum.
我知道我可以使用,thing.type.toString() == Type[Type.NEW]但这很麻烦,而且似乎使枚举类型注释变得混乱和误导,这违背了它的目的。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 stringis way too broad and error prone.
这让我得到了我想要的用法,但类型注释string太宽泛且容易出错。
I'm a bit surprised that a superset of JavaScript doesn't have string based enums. Am I missing something? Is there a different way this can be done?
我有点惊讶 JavaScript 的超集没有基于字符串的枚举。我错过了什么吗?有没有不同的方法可以做到这一点?
Update TS 1.8
更新 TS 1.8
Using string literal typesis 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 enumswill eventually make the roadmap.
这有效,但需要大量样板文件,足以让我大部分时间不使用它。现在,我希望提案string enums最终能够制定路线图。
Update TS 2.1
更新 TS 2.1
The new keyoftype lookupallows for the string literal type to be generated from the keys of a const or namespace, which makes the definition a littleless 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! The above example becomes:
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
This looks nearlyperfect, but there's still some heartache:
这看起来几乎完美,但仍有一些心痛:
- You stillhave to write the value twice, ie
OLD = "OLD", and there's no validation that you don't have a typo, likeNEW = "MEW"... this has already bitten me in real code. 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 Colorreplaced by string literal types work fine...
- 你仍然需要写两次值,即
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替换为字符串文字类型的等效代码工作正常......
Yeah, I think I have OCD about this, I just want my perfect JS enums. :)
是的,我想我对此有强迫症,我只想要我完美的 JS 枚举。:)
采纳答案by Felipe Sabino
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,则可以通过将枚举的值转换为any.
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 enumsalready, so the cast to anywould 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.
打字稿2.4已经内置字符串枚举的支持了,所以要转换成any将不再是必要的,你可以实现它,而无需使用的String Literal Union Type,这是确定的验证和自动完成,但不是那么好可读性和重构,这取决于使用场景。
回答by Robert Grabiński
TS 2.9.2
My solution:
TS 2.9.2
我的解决方案:
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]:
...
}
回答by basarat
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 literalsin a union. More : https://basarat.gitbooks.io/typescript/content/docs/tips/stringEnums.html
这些在union中称为字符串文字。更多:https: //basarat.gitbooks.io/typescript/content/docs/tips/stringEnums.html
回答by Ryan Burbidge
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;
}

