有没有办法让TypeScript枚举与JSON中的字符串兼容?
例如:
enum Type { NEW, OLD }
interface Thing { type: Type }
let thing:Thing = JSON.parse('{"type": "NEW"}');
alert(thing.type == Type.NEW); // false
我希望 {/ em> 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
使用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或命名空间的键生成字符串文字类型,这使得 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 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"
,并且没有验证你没有输入错误,例如{{1} } ...这已经在实际代码中咬了我。有一些奇怪的东西(也许是错误?),如何对枚举进行类型检查,它不仅仅是一个字符串文字类型的简写,这才是真正正确的。我遇到的一些问题:
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'.
替换为字符串文字类型的等效代码正常工作......
是的,我认为我对此有OCD,我只想要完美的JS枚举。 :)
答案 0 :(得分:10)
如果您在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
的情况下实现它,这对于验证和自动完成是可以的,但不是这样有利于可读性和重构,具体取决于使用场景。
答案 1 :(得分:2)
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]:
...
}
答案 2 :(得分:2)
万一有人在 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 枚举。 :)
keyof typeof enumObj
关于,
<块引用>将枚举颜色替换为字符串文字类型的等效代码工作正常...
在链式连接中使用 typeof
和 keyof
运算符。
type ColorKeys = keyof typeof Color
type ColorMap = { [P in ColorKeys]: number; } // will have strongly typed keys
访问 any
时不再隐含 map: ColorMap
。
这也适用于数字枚举(可以(并且通常应该)是 const
)。
来自Typescript Handbook - Enums at compile time:
即使枚举是在运行时存在的真实对象,keyof 关键字的工作方式与您对典型对象的预期不同。相反,使用 keyof typeof 获取将所有 Enum 键表示为字符串的 Type。
ts-enum-util
查看 ts-enum-util
,它提供了(可能)您所有与枚举相关的需求的强类型接口。
答案 3 :(得分:0)
但类型注释字符串过于宽泛且容易出错。
同意。一个快速的解决方法(如果您拥有丰富的代码生成,您可以自动执行此操作):
interface Thing { type: "NEW" | "OLD" }
这些在 union 中称为字符串文字。更多:https://basarat.gitbooks.io/typescript/content/docs/tips/stringEnums.html
答案 4 :(得分:0)
我一直在使用转换器功能作为权宜之计。希望这个主题达成一个解决方案: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;
}