如何在TypeScript中将字符串转换为enum ?

我在TypeScript中定义了以下enum:

enum Color{
Red, Green
}

现在在我的函数中,我以字符串的形式接收颜色。我尝试了以下代码:

var green= "Green";
var color : Color = <Color>green; // Error: can't convert string to enum

如何将该值转换为enum?

557492 次浏览

我得到了它的工作使用以下代码。

var green= "Green";
var color : Color= <Color>Color[green];

TypeScript 0.9中的枚举是基于字符串+数字的。简单转换不需要类型断言:

enum Color{
Red, Green
}


// To String
var green: string = Color[Color.Green];


// To Enum / number
var color : Color = Color[green];

试着在线试试

我在我的OSS书籍https://basarat.gitbook.io/typescript/type-system/enums中有关于这个和其他Enum模式的文档

这个注释与basarat的回答有关,而不是最初的问题。

我在自己的项目中遇到了一个奇怪的问题,编译器给出了一个大致相当于“不能将字符串转换为颜色”的错误,使用这段代码的等价物:

var colorId = myOtherObject.colorId; // value "Green";
var color: Color = <Color>Color[colorId]; // TSC error here: Cannot convert string to Color.

我发现编译器的类型推断变得混乱,它认为colorId是一个enum值,而不是一个ID。为了解决这个问题,我必须将ID转换为字符串:

var colorId = <string>myOtherObject.colorId; // Force string value here
var color: Color = Color[colorId]; // Fixes lookup here.

我不确定是什么导致了这个问题,但我会在这里留下这张便条,以防有人遇到和我一样的问题。

我还遇到了同样的编译器错误。只是Sly_cardinal方法的一个稍短的变种。

var color: Color = Color[<string>colorId];

如果TypeScript编译器知道变量的类型是字符串,那么这是可行的:

let colorName : string = "Green";
let color : Color = Color[colorName];

否则,你应该显式地将其转换为字符串(以避免编译器警告):

let colorName : any = "Green";
let color : Color = Color["" + colorName];

在运行时,两个解决方案都可以工作。

打印稿1.倍

如果你确定输入字符串与Color enum完全匹配,那么使用:

const color: Color = (<any>Color)["Red"];

在输入字符串可能不匹配Enum的情况下,使用:

const mayBeColor: Color | undefined = (<any>Color)["WrongInput"];
if (mayBeColor !== undefined){
// TypeScript will understand that mayBeColor is of type Color here
}

<一个href = " http://www.typescriptlang.org/play/ src = enum % 20颜色% 7 b % 0 a % 20% 20% 20% 20红色% 2 c % 20绿色% 0 a % d % 0 7 aconst % 20 % % 20 3颜色3 d % % 20% 20% 3藤% 3 ecolor % 5 b % 22绿色% 22% 5 d % 3 b % 0 aconsole.log(% 22颜色% 3 a % 20% 22% 20% 2 b % 20颜色)% 3 b % 0 a % 0 aconst % 20 maybecolor % 3 7 c % 20颜色% 20% % 20未定义的3 d % % 20% 20% 3藤% 3 ecolor % 5 b % 22 wronginput % 22% 5 d % 3 b % 0 aconsole.log(% 22非% 20现有% 20颜色% 3 a % 20% 22% 20% 2 b % 20 maybecolor) % 3 b % 0 a % 0 aconst % 20 maybecolor2 % 3 7 c % 20颜色% 20% % 20未定义的3 d % % 20% 20% 3藤% 3 ecolor % 5 b % 22绿色% 22% 5 d % 3 b % 0 aconsole.log(% 22现有% 20颜色% % 20% 22% 2 bmaybecolor2 3) % 3 b noreferrer“rel = >操场< / >


如果不将enum类型转换为<any>类型,TypeScript将显示错误:

元素隐式具有“any”类型,因为索引表达式不是类型“number”。

这意味着默认情况下,TypeScript Enum类型与数字索引一起工作。 let c = Color[0],但不是像let c = Color["string"]这样的字符串索引。这是微软团队针对更普遍的问题对象字符串索引 . .

的一个已知限制

2.打印稿x-4x

TypeScript移到了keyof typeof概念。

如果some使用字符串值enum:

enum Color {
Green = "GRN",
Red = "RD"
}

然后有语言解决方案映射键值(颜色。绿色→"GRN"),但没有简单的方法来做相反的事情("GRN"→Color.Green)。从# EYZ0:

请记住,string enum成员不会得到反向映射

一种可能的解决方案是手动检查值并将值强制转换为enum。请注意,它只适用于字符串枚举。

function enumFromStringValue<T> (enm: { [s: string]: T}, value: string): T | undefined {
return (Object.values(enm) as unknown as string[]).includes(value)
? value as unknown as T
: undefined;
}


enumFromStringValue(Color, "RD"); // Color.Red
enumFromStringValue(Color, "UNKNOWN"); // undefined
enumFromStringValue(Color, "Red"); // undefined

从Typescript 2.1开始,enum中的字符串键都是强类型的。keyof typeof用于获取可用字符串键(1)的信息:

enum Color{
Red, Green
}


let typedColor: Color = Color.Green;
let typedColorString: keyof typeof Color = "Green";


// Error "Black is not assignable ..." (indexing using Color["Black"] will return undefined runtime)
typedColorString = "Black";


// Error "Type 'string' is not assignable ..." (indexing works runtime)
let letColorString = "Red";
typedColorString = letColorString;


// Works fine
typedColorString = "Red";


// Works fine
const constColorString = "Red";
typedColorString = constColorString


// Works fine (thanks @SergeyT)
let letColorString = "Red";
typedColorString = letColorString as keyof typeof Color;


typedColor = Color[typedColorString];

https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types

在这个问题中有很多混合信息,所以让我们讨论TypeScript 2的整个实现。x+在尼克的指南中使用枚举模型与TypeScript

本指南适用于:创建客户端代码的人,这些客户端代码从服务器获取一组已知字符串,这些字符串可以方便地在客户端建模为Enum。

定义枚举

让我们从枚举开始。它应该看起来像这样:

export enum IssueType {
REPS = 'REPS',
FETCH = 'FETCH',
ACTION = 'ACTION',
UNKNOWN = 'UNKNOWN',
}

这里需要注意两点:

  1. 我们显式地将这些声明为字符串支持的枚举情况,这允许我们用字符串来实例化它们,而不是其他一些不相关的数字。

  2. 我们添加了一个在服务器模型上可能存在也可能不存在的选项:UNKNOWN。如果您愿意,可以将其处理为undefined,但我喜欢尽可能避免在类型上使用| undefined,以简化处理。

使用UNKNOWN大小写的好处是,你可以在代码中非常明显地看到它,并为未知enum大小写设置亮红色和闪烁的样式,这样你就知道你没有正确地处理某些事情。

解析枚举

您可以在另一个模型中使用这个枚举,或者单独使用这个枚举,但是您必须将字符串-y类型的枚举从JSON或XML (ha)解析为强类型的对应对象。当嵌入到另一个模型中时,这个解析器存在于类构造函数中。

parseIssueType(typeString: string): IssueType {
const type = IssueType[typeString];
if (type === undefined) {
return IssueType.UNKNOWN;
}


return type;
}

如果枚举被正确地解析,它将以正确的类型结束。否则,它将是undefined,您可以拦截它并返回UNKNOWN情况。如果您喜欢使用undefined作为未知情况,则可以直接返回尝试的enum解析的任何结果。

从这里开始,只需要使用parse函数和使用新创建的强类型变量。

const strongIssueType: IssueType = parseIssueType('ACTION');
// IssueType.ACTION
const wrongIssueType: IssueType = parseIssueType('UNEXPECTED');
// IssueType.UNKNOWN
enum Color{
Red, Green
}


// To String
var green: string = Color[Color.Green];


// To Enum / number
var color : Color = Color[green as keyof typeof Color]; //Works with --noImplicitAny

这个例子使用TypeScript中的--noImplicitAny

< p >来源:< br > # EYZ0 # EYZ1 < / p >

# EYZ0

enum MyEnum {
First,
Second,
Three
}

示例使用

const parsed = Parser.parseEnum('FiRsT', MyEnum);
// parsed = MyEnum.First


const parsedInvalid= Parser.parseEnum('other', MyEnum);
// parsedInvalid = undefined

# EYZ0

class Parser {
public static parseEnum<T>(value: string, enumType: T): T[keyof T] | undefined {
if (!value) {
return undefined;
}


for (const property in enumType) {
const enumMember = enumType[property];
if (typeof enumMember === 'string') {
if (enumMember.toUpperCase() === value.toUpperCase()) {
const key = enumMember as string as keyof typeof enumType;
return enumType[key];
}
}
}
return undefined;
}
}

我需要知道如何循环枚举值(正在测试许多排列的几个enum),我发现这工作得很好:

export enum Environment {
Prod = "http://asdf.com",
Stage = "http://asdf1234.com",
Test = "http://asdfasdf.example.com"
}


Object.keys(Environment).forEach((environmentKeyValue) => {
const env = Environment[environmentKeyValue as keyof typeof Environment]
// env is now equivalent to Environment.Prod, Environment.Stage, or Environment.Test
}

来源:# EYZ0

以这种方式创建的枚举被编译成一个对象,该对象同时存储正向的(name -> value)映射和反向的(value -> name)映射。从这张chrome devtools截图中我们可以看到:

enter image description here

下面是一个关于对偶映射如何工作以及如何从一个映射转换到另一个映射的例子:

enum Color{
Red, Green
}
// To Number
var greenNr: number = Color['Green'];
console.log(greenNr); // logs 1


// To String
var greenString: string = Color[Color['Green']];  // or Color[Color[1]
console.log(greenString); // logs Green


// In your example


// recieve as Color.green instead of the string green
var green: string = Color[Color.Green];


// obtain the enum number value which corresponds to the Color.green property
var color: Color = (<any>Color)[green];


console.log(color); // logs 1

试试这个

var color: color =(任何颜色)["绿色];

这对于3.5.3版本来说很好

我正在寻找一个答案,可以从string得到enum,但在我的情况下,枚举值有不同的字符串值对应。OP有一个简单的枚举Color,但我有一些不同的东西:

enum Gender {
Male = 'Male',
Female = 'Female',
Other = 'Other',
CantTell = "Can't tell"
}

当您尝试用"Can't tell"字符串解析Gender.CantTell时,它将返回带有原始答案的undefined

另一个答案

基本上,受到这个答案的强烈启发,我想出了另一个答案:

export const stringToEnumValue = <ET, T>(enumObj: ET, str: string): T =>
(enumObj as any)[Object.keys(enumObj).filter(k => (enumObj as any)[k] === str)[0]];

笔记

  • 我们取filter中的第一个结果,假设客户端从enum中传递一个有效的字符串。如果不是这样,则返回undefined
  • 我们将enumObj转换为any,因为在TypeScript 3.0+(目前使用TypeScript 3.5)中,enumObj被解析为unknown

使用实例

const cantTellStr = "Can't tell";


const cantTellEnumValue = stringToEnumValue<typeof Gender, Gender>(Gender, cantTellStr);
console.log(cantTellEnumValue); // Can't tell

注意:而且,正如有人在评论中指出的那样,我还想使用noImplicitAny

更新版本

没有强制转换到any和正确的类型。

export const stringToEnumValue = <T, K extends keyof T>(enumObj: T, value: string): T[keyof T] | undefined =>
enumObj[Object.keys(enumObj).filter((k) => enumObj[k as K].toString() === value)[0] as keyof typeof enumObj];

此外,更新版本有一个更简单的方法来调用它,更可读:

stringToEnumValue(Gender, "Can't tell");

如果您为枚举提供字符串值,则直接强制转换可以很好地工作。

enum Color {
Green = "Green",
Red = "Red"
}


const color = "Green";
const colorEnum = color as Color;
如果你使用typescript:

.以上许多解决方案可能无效或过于复杂

情况:字符串与enum值不相同(大小写不同)

enum Color {
Green = "green",
Red = "red"
}

只使用:

const color = "green" as Color

请注意,这并不能保证一个有效的enum。

如果您正在使用名称空间来扩展枚举的功能,那么您还可以这样做

    enum Color {
Red, Green
}


export namespace Color {
export function getInstance(color: string) : Color {
if(color == 'Red') {
return Color.Red;
} else if (color == 'Green') {
return Color.Green;
}
}
}

像这样使用它

  Color.getInstance('Red');

其他的变化可以是

const green= "Green";


const color : Color = Color[green] as Color;

简单的方法

enum Color { Red, Green }


const c1 = Color["Red"]
const redStr = "Red" // important: use `const`, not mutable `let`
const c2 = Color[redStr]

这对数字字符串枚举都有效。不需要使用类型的断言

未知enum字符串

简单,不安全的变体
const redStrWide: string = "Red" // wide, unspecific typed string
const c3 = Color[redStrWide as keyof typeof Color]
带有检查的安全变体
const isEnumName = <T>(str: string, _enum: T): str is Extract<keyof T, string> =>
str in _enum
const enumFromName = <T>(name: string, _enum: T) => {
if (!isEnumName(name, _enum)) throw Error() // here fail fast as an example
return _enum[name]
}
const c4 = enumFromName(redStrWide, Color)

转换字符串enum值

字符串枚举没有反向映射(与数字相比)。我们可以创建一个查找助手来将枚举值字符串转换为枚举类型:

enum ColorStr { Red = "red", Green = "green" }


const c5_by_name = ColorStr["Red"] // ✅ this works
const c5_by_value_error = ColorStr["red"] // ❌ , but this not


const enumFromValue = <T extends Record<string, string>>(val: string, _enum: T) => {
const enumName = (Object.keys(_enum) as Array<keyof T>).find(k => _enum[k] === val)
if (!enumName) throw Error() // here fail fast as an example
return _enum[enumName]
}


const c5 = enumFromValue("red", ColorStr)

<一个href = " https://www.typescriptlang.org/play?jsx=0代码/ PTCwCgQAgRgSigZwJYFsAOAbApogLlAIbroBOA9oQMYAWEIE2AdgK6pQDC5m5pUA3lABK2ACYAaKAHFS2ZlAC + EKuSb4oVGFAC8nbrwDaAIhGijAXWWr1s0QGU8fXSbFGo0NOl55CTPAC4oFkRsKAADFTU8MMkmcgJUFh8AIxxwnGirKI0AJh09HlIDWwdSS3B6MEhgKByEFiYAaziAdyYoZjYkR2QmAHNESqybMVKAdWRRbED8Ul6 + -JczdxqWyexJBsR0bCpkADNkKig8AE8d0W65-ohK2sIEFAwcTbVCfdCAN0I53zxhghUADM + S4hWKo0cEymREQUEa2FO5H2J3O2GRBV45TuOWSj3eXx +码+ AKgyEQAFFWKgAHKEVChXQAHgAKgA + AAUsxmPX6kgA + p1UIEWXAeXxyVAKQAPRzUPBMhFIlEsySzeZsnRsiBQXVXMntQXU0lCgBiFFp9MZUFZnKYVvF8wFQpFCG0mv4Or1BygHIAhOSqWw6QyOfaGc7qXAEHgaBQWlLSBRSByENAaNhZFB9oRkJhs4R1IWiO1sNL6VhsF7dbI8CxSIahQZw9hykoKuBIuoqAAWfJmi0h7AckpQ9aSMG8OC3ap3IEILhMT6Zgjq-odalQb6YFihIbgIWY0ilATCMSLWxGSQyOTtZx9WTMNzt0lUACs-KVoP0x8cxlMFgrFAgCg5CcNCSi0vCNIMnbWICH6Zsm36FKUxiXuYQGADLkUCSMkSRgZKcT-B2XYEAO5CoAAaoQO7WqyHSyswohwiIKikKITJrn0aq8n0bKctujp8lARpsK6WoCNWGhwRuwZWvkHIAPLJAAVrseAAHRKogHKiagCDFgAgkmhCnIqiIYuycAaYcTCiByjQSXpBiNBh2juVuNHTuAepkii-pCkOMZxuQCYUkmvCpkBGZZjmeYFkWcK + AxFY4FJtb1o21IGIFVptjOpEaG+-bUuaFHUbRHJGJeE4-qUcBAA" rel="noreferrer">游乐场样本 .

# EYZ0办法

enum Color{ RED, GREEN }


const color = 'RED' as Color;

容易peasy……柠檬捏的!

TS 3.9.x

var color : Color = Color[green as unknown as keyof typeof Color];

如果你对类型保护感兴趣,否则就是string(这就是我遇到这个问题的原因),这可能对你有用:

enum CurrencyCode {
cad = "cad",
eur = "eur",
gbp = "gbp",
jpy = "jpy",
usd = "usd",
}


const createEnumChecker = <T extends string, TEnumValue extends string>(
enumVariable: { [key in T]: TEnumValue }
) => {
const enumValues = Object.values(enumVariable);
return (value: string | number | boolean): value is TEnumValue =>
enumValues.includes(value);
};


const isCurrencyCode = createEnumChecker(CurrencyCode);


const input: string = 'gbp';


let verifiedCurrencyCode: CurrencyCode | null = null;
// verifiedCurrencyCode = input;
// ^ TypeError: Type 'string' is not assignable to type 'CurrencyCode | null'.


if (isCurrencyCode(input)) {
verifiedCurrencyCode = input; // No Type Error 🎉
}


解决方案从这个github的问题讨论通用枚举

如果你正在处理TypeScript 4.1+和字符串enum,并且你想要一个简单的具有编译时和运行时安全的字符串到enum转换器,下面的工作很好:

export const asEnum = <
T extends { [key: string]: string },
K extends keyof T & string
>(
enumObject: T,
value: `${T[K]}`
): T[K] => {
if (Object.values(enumObject).includes(value)) {
return (value as unknown) as T[K];
} else {
throw new Error('Value provided was not found in Enum');
}
};


enum Test {
hey = 'HEY',
}


const test1 = asEnum(Test, 'HEY');   // no complaints here
const test2 = asEnum(Test, 'HE');    // compile-time error
const test3 = asEnum(Test, 'HE' as any); // run-time error

这些答案对我来说都太复杂了……

您可以简单地在枚举上创建一个解析函数,期望其中一个键作为参数。添加新颜色时,不需要进行其他更改

enum Color { red, green}


// Get the keys 'red' | 'green' (but not 'parse')
type ColorKey = keyof Omit<typeof Color, 'parse'>;


namespace Color {
export function parse(colorName: ColorKey ) {
return Color[colorName];
}
}


// The key 'red' exists as an enum so no warning is given
Color.parse('red');  // == Colors.red


// Without the 'any' cast you would get a compile-time warning
// Because 'foo' is not one of the keys in the enum
Color.parse('foo' as any); // == undefined


// Creates warning:
// "Argument of type '"bar"' is not assignable to parameter of type '"red" | "green"'"
Color.parse('bar');

它在TypeScript 4.4.3 TS游乐场链接中为我工作。

  const stringToEnumValue = <T extends Record<string, string>, K extends keyof T>(
enumObj: T,
value: string,
): T[keyof T] | undefined =>
enumObj[
Object.keys(enumObj).filter(
(k) => enumObj[k as K].toString() === value,
)[0] as keyof typeof enumObj
];


enum Color {
Red = 'red',
Green = 'green',
}


const result1 = stringToEnumValue(Color, 'yellow'); // undefined
const result2 = stringToEnumValue(Color, 'green'); // Color.Green


console.log(result1) // undefined = undefined
console.log(result2) // Color.Green = "green"


对于Typescript >= 4,这段代码工作:

enum Color{
Red, Green
}


// Conversion :
var green= "Green";
var color : Color = green as unknown as Color;
< p > TL;博士: : < / p >
  • 创建一个函数,解析字符串值并将其转换为枚举。
  • 如果你需要给定值的键名,不要使用TS enum。

首先,枚举是人类可读的名称和值之间的映射,这就是它的用途。

< p >默认值: TS默认情况下确保枚举中定义的键值是唯一的

enum Color {
Red, Green
}

等于

enum Color {
Red = 0,
Green = 1
}

转译的js代码都将

"use strict";
var Color;
(function (Color) {
Color[Color["Red"] = 0] = "Red";
Color[Color["Green"] = 1] = "Green";
})(Color || (Color = {}));

由于这是不可读的,下面是一旦创建的结果对象:

{0: 'Red', 1: 'Green', Red: 0, Green: 1}

该对象具有字符串和数字属性(不可能有任何冲突,因为您不能将枚举键定义为数字)。TS足够酷,可以生成一个包含映射键->值和价值→关键

感谢上帝,这是一个双射映射,即每个唯一值都有它的唯一键(因此反过来也是正确的)

现在麻烦来了,如果我强制使用相同的值呢?

enum Color {
Red = 0,
Green = 0
}

这是最终创建的js对象

{0: 'Green', Red: 0, Green: 0}

我们不再有双射,(这是满射),没有魔法映射0 : ['Green', 'Red']。只有0 : 'Green',我们失去了0 : 'Red'

提示:TS会一直尝试将反向映射(value ->当值为数字时,键)。

现在你可能知道,你也可以在一个枚举中定义字符串值,让我们只改变Green值为"Green"

enum Color {
Red = 0,
Green = "GREEN"
}

这是生成的js对象

{0: 'Red', Red: 0, Green: 'GREEN'}
如你所见,Typescript没有生成映射值->关键。 它不会,因为您可能会在值和键名之间发生冲突。记住:键不能是数字,因此当值是数字时就没有碰撞的风险

这使您明白,您不应该依赖于值->枚举的键映射。映射可能根本不存在或不准确。

同样,枚举是(也只能被视为)人类可读的值名称。在某些情况下中,ts甚至不会产生任何反向映射。这就是定义枚举const时的情况。

const枚举是一个纯编译时的枚举,TS会在编译时将枚举的使用替换为其对应的值

# EYZ0:

const enum Color {
Red = 0,
Green = "GREEN"
}

被转译为

"use strict";

所以只是说…没什么,因为“使用严格”;;跟我们写的都没有关系。

下面是一个用法的相同示例:

const enum Color {
Red = 0,
Green = "GREEN"
}
console.log(Color.Green);

被转译为

"use strict";
console.log("GREEN" /* Green */);

如你所见,颜色。Green被" Green "在转发器的位置。

回到最初的问题,如何将字符串转换为enum ?

< >强解析器解决方案: 我很抱歉,但我推荐的唯一干净的方法是写一个函数,使用开关大小写是一个聪明的方法来实现这一点

function parseColorName(color: string): Color {
switch (color) {
case 'Red': return Color.Red;
case 'Green': return Color.Green;
default: throw new Error('unknown color');
}
}

自定义enum解决方案:

请注意,TS枚举是不透明的,这意味着编译器无法正确地键入值。出于这个原因(特别是当你需要使用反向映射时),我建议你自己做枚举,如下所示:

export const ColorType = {
RED: 'Red',
GREEN: 'Green',
} as const;


export type ColorType = typeof ColorType[keyof typeof ColorType];

下面是安全的(color只能接受一个有效的已知值)。简而言之,您依赖于字符串联合而不是枚举值。

const color: ColorType= "Green";
// And if you need to create a color from the enum like value:
const anotherColor: ColorType = ColorType.RED;

提供的大多数答案都没有对Enums提供广泛的支持。允许OP请求只从字符串值中获取Enum,但是Enum也允许其他值。

interface StandardEnum<T> {
[id: string]: T | string;
[nu: number]: string;
}


/**
* Converts the given representation of the value of one enumerated constant to an equivalent enumerated type.
*
* @param type - An enumeration type
* @param value - A value to convert
*/
export const genericValueToEnum = <T, K extends StandardEnum<T>> (
type: StandardEnum<T>,
value: K[keyof K]
): T | undefined => {
const keys = Object.keys(type); // ...but, not really.
const values = Object.values(type)
// Filter enum member names because `Object.values()` includes them.
.filter((value) => !(
typeof value === 'string' &&
keys.includes(value) &&
type[value] !== value
));


return values.includes(value)
? value as unknown as T
: undefined;
}

这将适用于所有枚举,无论它们多么复杂(或奇怪),只要它们没有被标记。

enum OddEnum {
None = -1,
No = 0,
Yes = 1,
Twenty = '20'
Other = 'Other',
MORE = 'More',
};


genericValueToEnum(OddEnum, -1); // => [-1] as OddEnum;
genericValueToEnum(OddEnum, 'Other'); // => ['Other'] as OddEnum;
genericValueToEnum(OddEnum, 'MORE'); // => undefined;

几乎所有答案都使用不安全的类型转换(asis)。这一条没有:


enum Color {
Red = "red",
Green = "green"
}


const colorsMap = new Map<string,Color>(Object.values(Color).map((v) => [v,v]))


function parseColor(volumeData: string): Color | undefined {
return colorsMap.get(volumeData)
}


const color = parseColor("red")