How do I convert a string to enum in TypeScript?
TypescriptTypescript Problem Overview
I have defined the following enum in TypeScript:
enum Color{
Red, Green
}
Now in my function I receive color as a string. I have tried the following code:
var green= "Green";
var color : Color = <Color>green; // Error: can't convert string to enum
How can I convert that value to an enum?
Typescript Solutions
Solution 1 - Typescript
Enums in TypeScript 0.9 are string+number based. You should not need type assertion for simple conversions:
enum Color{
Red, Green
}
// To String
var green: string = Color[Color.Green];
// To Enum / number
var color : Color = Color[green];
I have documention about this and other Enum patterns in my OSS book : https://basarat.gitbook.io/typescript/type-system/enums
Solution 2 - Typescript
As of Typescript 2.1 string keys in enums are strongly typed. keyof typeof
is used to get info about available string keys ([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][1]
[1]: https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types "Typescript index types"
Solution 3 - Typescript
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
This example works with --noImplicitAny
in TypeScript
Sources:
https://github.com/Microsoft/TypeScript/issues/13775#issuecomment-276381229 https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types
Solution 4 - Typescript
If you provide string values to your enum, a straight cast works just fine.
enum Color {
Green = "Green",
Red = "Red"
}
const color = "Green";
const colorEnum = color as Color;
Solution 5 - Typescript
Given you use typescript: Many of the solutions above might not work or are overly complex.
Situation: The strings are not the same as the enum values (casing differs)
enum Color {
Green = "green",
Red = "red"
}
Just use:
const color = "green" as Color
Please note that this does not guarantee a valid enum.
Solution 6 - Typescript
Typescript 1.x
If you are sure that an input string has an exact match with Color enum then use:
const color: Color = (<any>Color)["Red"];
In the case where an input string may not match Enum, use:
const mayBeColor: Color | undefined = (<any>Color)["WrongInput"];
if (mayBeColor !== undefined){
// TypeScript will understand that mayBeColor is of type Color here
}
If we do not cast enum
to <any>
type then TypeScript will show the error:
> Element implicitly has 'any' type because index expression is not of type 'number'.
It means that by default the TypeScript Enum type works with number indexes, i.e.
let c = Color[0]
, but not with string indexes like let c = Color["string"]
. This is a known restriction by the Microsoft team for the more general issue Object string indexes.
Typescript 2.x-4x
TypeScript moved to the keyof typeof
concept.
If some uses string value enums:
enum Color {
Green = "GRN",
Red = "RD"
}
then there is the language solution to map keys to values (Color.Green -> "GRN") just by accessing an enum member, but there is no simple way to do the reverse ("GRN" -> Color.Green). From reverse-mapping:
> Keep in mind that string enum members do not get a reverse mapping > generated at all.
A possible solution is to manually check values and cast the value to the enum. Please notice that it will work only with string enums.
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
Solution 7 - Typescript
I got it working using the following code.
var green= "Green";
var color : Color= <Color>Color[green];
Solution 8 - Typescript
This note relates to basarat's answer, not the original question.
I had an odd issue in my own project where the compiler was giving an error roughly equivalent to "cannot convert string to Color" using the equivalent of this code:
var colorId = myOtherObject.colorId; // value "Green";
var color: Color = <Color>Color[colorId]; // TSC error here: Cannot convert string to Color.
I found that the compiler type inferencing was getting confused and it thought that colorId
was an enum value and not an ID. To fix the problem I had to cast the ID as a string:
var colorId = <string>myOtherObject.colorId; // Force string value here
var color: Color = Color[colorId]; // Fixes lookup here.
I'm not sure what caused the issue but I'll leave this note here in case anyone runs into the same problem I did.
Solution 9 - Typescript
Simplest approach
enum Color { Red, Green }
const c1 = Color["Red"]
const redStr = "Red" // important: use `const`, not mutable `let`
const c2 = Color[redStr]
This works both for numeric and string enums. No need to use a type assertion.
Unknown enum strings
Simple, unsafe variant
const redStrWide: string = "Red" // wide, unspecific typed string
const c3 = Color[redStrWide as keyof typeof Color]
Safe variant with checks
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)
Convert string enum values
String enums don't have a reverse mapping (in contrast to numeric ones). We can create a lookup helper to convert an enum value string to an enum type:
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)
Solution 10 - Typescript
I also ran into the same compiler error. Just a slight shorter variation of Sly_cardinal's approach.
var color: Color = Color[<string>colorId];
Solution 11 - Typescript
If the TypeScript compiler knows that the type of variable is string then this works:
let colorName : string = "Green";
let color : Color = Color[colorName];
Otherwise you should explicitly convert it to a string (to avoid compiler warnings):
let colorName : any = "Green";
let color : Color = Color["" + colorName];
At runtime both solutions will work.
Solution 12 - Typescript
I was looking for an answer that can get an enum
from a string
, but in my case, the enums values had different string values counterpart. The OP had a simple enum for Color
, but I had something different:
enum Gender {
Male = 'Male',
Female = 'Female',
Other = 'Other',
CantTell = "Can't tell"
}
When you try to resolve Gender.CantTell
with a "Can't tell"
string, it returns undefined
with the original answer.
Another answer
Basically, I came up with another answer, strongly inspired by this answer:
export const stringToEnumValue = <ET, T>(enumObj: ET, str: string): T =>
(enumObj as any)[Object.keys(enumObj).filter(k => (enumObj as any)[k] === str)[0]];
Notes
- We take the first result of
filter
, assuming the client is passing a valid string from the enum. If it's not the case,undefined
will be returned. - We cast
enumObj
toany
, because with TypeScript 3.0+ (currently using TypeScript 3.5), theenumObj
is resolved asunknown
.
Example of Use
const cantTellStr = "Can't tell";
const cantTellEnumValue = stringToEnumValue<typeof Gender, Gender>(Gender, cantTellStr);
console.log(cantTellEnumValue); // Can't tell
Note: And, as someone pointed out in a comment, I also wanted to use the noImplicitAny
.
Updated version
No cast to any
and proper typings.
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];
Also, the updated version has a easier way to call it and is more readable:
stringToEnumValue(Gender, "Can't tell");
Solution 13 - Typescript
I needed to know how to loop over enum values (was testing lots of permutations of several enums) and I found this to work well:
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
}
Source: https://blog.mikeski.net/development/javascript/typescript-enums-to-from-string/
Solution 14 - Typescript
There's a lot of mixed information in this question, so let's cover the whole implementation for TypeScript 2.x+ in Nick's Guide to Using Enums in Models with TypeScript.
This guide is for: people who are creating client-side code that's ingesting a set of known strings from the server that would be conveniently modeled as an Enum on the client side.
Define the enum
Let's start with the enum. It should look something like this:
export enum IssueType {
REPS = 'REPS',
FETCH = 'FETCH',
ACTION = 'ACTION',
UNKNOWN = 'UNKNOWN',
}
Two thing of note here:
-
We're explicitly declaring these as string-backed enum cases which allows us to instantiate them with strings, not some other unrelated numbers.
-
We've added an option that may or may not exist on our server model:
UNKNOWN
. This can be handled asundefined
if you prefer, but I like to avoid| undefined
on types whenever possible to simplify handling.
The great thing about having an UNKNOWN
case is that you can be really obvious about it in code and make styles for unknown enum cases bright red and blinky so you know you're not handling something correctly.
Parse the enum
You might be using this enum embedded in another model, or all alone, but you're going to have to parse the string-y typed enum from JSON or XML (ha) into your strongly typed counterpart. When embedded in another model, this parser lives in the class constructor.
parseIssueType(typeString: string): IssueType {
const type = IssueType[typeString];
if (type === undefined) {
return IssueType.UNKNOWN;
}
return type;
}
If the enum is properly parsed, it'll end up as the proper type. Otherwise, it'll be undefined
and you can intercept it and return your UNKNOWN
case. If you prefer using undefined
as your unknown case, you can just return any result from the attempted enum parsing.
From there, it's only a matter of using the parse function and using your newly strong typed variable.
const strongIssueType: IssueType = parseIssueType('ACTION');
// IssueType.ACTION
const wrongIssueType: IssueType = parseIssueType('UNEXPECTED');
// IssueType.UNKNOWN
Solution 15 - Typescript
For TS 3.9.x
var color : Color = Color[green as unknown as keyof typeof Color];
Solution 16 - Typescript
If you're dealing with TypeScript 4.1+ and string enums, and you want a simple string-to-Enum converter with compile-time and run-time safety, the following works well:
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
Solution 17 - Typescript
Enum
enum MyEnum {
First,
Second,
Three
}
Sample usage
const parsed = Parser.parseEnum('FiRsT', MyEnum);
// parsed = MyEnum.First
const parsedInvalid= Parser.parseEnum('other', MyEnum);
// parsedInvalid = undefined
Ignore case sensitive parse
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;
}
}
Solution 18 - Typescript
Enums created in the way you did are compiled into an object that stores both forward (name -> value)
and reverse (value -> name)
mappings. As we can observe from this chrome devtools screenshot:
Here is an example of how dual mapping works and how to cast from one to another:
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
Solution 19 - Typescript
Try this > var color : Color = (Color as any)["Green];
That works fine for 3.5.3 version
Solution 20 - Typescript
Most of these answers seems overly complex to me...
You could simply create a parse function on the enum that expects one of the keys as arguments. When new colors are added no other changes are necessary
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');
Solution 21 - Typescript
Typescript 3.9 propsal
enum Color{ RED, GREEN }
const color = 'RED' as Color;
easy peasy... lemon squeezy!
Solution 22 - Typescript
If you're interested in type guarding an what would otherwise be a string
(which is how I came across this issue), this might work for you:
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 🎉
}
Solution is taken from this github issue discussing generic Enums
Solution 23 - Typescript
It works for me in TypeScript 4.4.3 TS Playground link.
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"
Solution 24 - Typescript
other variation can be
const green= "Green";
const color : Color = Color[green] as Color;
Solution 25 - Typescript
TL;DR: Either:
- Make a function which parse and convert the string value into an enum.
- If you need the key name given the value, don't use a TS enum.
At first, an enum is a mapping between a human readable name and a value, this is how it is made for.
Default values: TS will by default ensure you do have a unique value for the defined keys of the enum.
This
enum Color {
Red, Green
}
Is equivalent to
enum Color {
Red = 0,
Green = 1
}
The transpiled js code of both will be
"use strict";
var Color;
(function (Color) {
Color[Color["Red"] = 0] = "Red";
Color[Color["Green"] = 1] = "Green";
})(Color || (Color = {}));
As this is unreadable, here is the resulting object once created:
{0: 'Red', 1: 'Green', Red: 0, Green: 1}
This object is having string and number properties (there cannot be any collision because you cannot defined an enum key as number). TS is cool enough to generate an object containing both the mapping key -> value and value -> key.
Thanks god this is a bijective mapping i.e. a every unique value is having it's unique key (and therefore the opposite is true as well)
Now comes the troubles, what if I force using the same value ?
enum Color {
Red = 0,
Green = 0
}
This is the resulting created js object
{0: 'Green', Red: 0, Green: 0}
We do not have the bijection anymore, (this is surjectif), there is no magic mapping 0 : ['Green', 'Red']
. Only 0 : 'Green'
and we lost the 0 : 'Red'
Takeway: TS will always try to put the reverse map (value -> key) when the values are numbers.
Now as you may know, you can also define string values within an enum, let's change only the Green value to "Green"
enum Color {
Red = 0,
Green = "GREEN"
}
Here is the resulting js object
{0: 'Red', Red: 0, Green: 'GREEN'}
As you can see, Typescript is not generating the mapping value -> key. And it will not because you might end up with a collision between a value and a key name. Remember: a key cannot be a number therefore when the value is a number there is no risk of collision.
This makes you understand that you should not rely on the value -> key mapping of an enum. The mapping could simply be inexistant or inaccurate.
Again, an enum is, and should only be considered as, a human readable name to a value. In some case ts will not even generate any reverse mapping at all. This is the case when you define an enum const.
A const enum is a pure compilation time enum, TS will replace the use of the enum with its corresponding value at the transpilation
For instance:
const enum Color {
Red = 0,
Green = "GREEN"
}
Is transpiled to
"use strict";
So just to say… nothing because "use strict"; is not even related to what we wrote.
Here is the same example with a usage:
const enum Color {
Red = 0,
Green = "GREEN"
}
console.log(Color.Green);
Is transpiled to
"use strict";
console.log("GREEN" /* Green */);
As you can see, the Color.Green is replaced by "GREEN" in place by the transpiler.
So back to the original question, how do you convert a string into an enum ?
Parser solution: I'm sorry but the only clean way I recommend is writing a function, using a switch case is a clever way to achieve this.
function parseColorName(color: string): Color {
switch (color) {
case 'Red': return Color.Red;
case 'Green': return Color.Green;
default: throw new Error('unknown color');
}
}
Custom enum solution:
Note that TS enums are opaque, meaning that there is no way for the compiler to type the value properly. For this reason (and especially when you need to use reverse mapping) I would recommend doing your own enum as follow:
export const ColorType = {
RED: 'Red',
GREEN: 'Green',
} as const;
export type ColorType = typeof ColorType[keyof typeof ColorType];
The following is safe (color
can only take a valid known value). In short, you are relying on string unions instead of an enum value.
const color: ColorType= "Green";
// And if you need to create a color from the enum like value:
const anotherColor: ColorType = ColorType.RED;
Solution 26 - Typescript
For Typescript >= 4 this code worked:
enum Color{
Red, Green
}
// Conversion :
var green= "Green";
var color : Color = green as unknown as Color;
Solution 27 - Typescript
If you are using namespaces to extend the functionality of your enum then you can also do something like
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;
}
}
}
and use it like this
Color.getInstance('Red');