774

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?

1
  • Came here trying to get the enum from a string. enum MyEnum { A = 1, B = 5 } String x = 'B'; MyEnum[x].toString() // value 5 MyEnum[x] // value B hope it helps Commented Dec 21, 2021 at 9:40

31 Answers 31

835

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];

Try it online

I have documentation about this and other Enum patterns in my OSS book : https://basarat.gitbook.io/typescript/type-system/enums

Make sure to use this if you are using the typescript flag --noImplicitAny:

var color : Color = Color[green as keyof typeof Color];
10
  • 242
    This doesn't work with --noImplicitAny (in VS unchecked "Allow implicit 'any' types"). It produces error TS7017: Index signature of object type implicitly has an 'any' type. For me this worked: var color: Color = (<any>Color)[green]; (tested with version 1.4)
    – Vojta
    Commented Mar 13, 2015 at 19:31
  • 3
    @Vojta said right. Its not working in VS 2012. This one worked but var color: Color = (<any>Color)[green];
    – Faisal Mq
    Commented Sep 29, 2015 at 10:02
  • 1
    Note that this won't work if the enum is defined as "const" Commented Oct 3, 2017 at 21:27
  • 3
    It doesn't work here either, the official documentation seems to confirm that: typescriptlang.org/docs/handbook/release-notes/… Commented Nov 25, 2017 at 15:11
  • 227
    Make sure to use this if --noImplicitAny var color : Color = Color[green as keyof typeof Color];
    – Jonas
    Commented May 10, 2019 at 10:48
293

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

5
  • 9
    So we can use typecast: let s = "Green"; let typedColor = <keyof typeof Color> s;
    – SergeyT
    Commented Jun 8, 2017 at 13:06
  • Yep, and replacing let with const will work without casting. Updated example to clarify this. Thanks @SergeyT
    – Victor
    Commented Jun 8, 2017 at 20:47
  • 4
    typedColorString = Color["Black"]; now returns error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'
    – Dominik
    Commented Sep 9, 2019 at 17:27
  • 36
    A one line answer: const color: Color = Color[colorString as keyof typeof Color];
    – cscan
    Commented Nov 6, 2019 at 19:06
  • @Dominik You need a string enum
    – Lanet
    Commented Feb 21, 2023 at 9:03
232

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;
2
  • 54
    This could be misleading as it doesn't guard against invalid colors. const colorEnum = "Blue" as Color won't error, and you would be left thinking that colorEnum is OK. But if you were to console.log it, you'd see "Blue". Artru's answer is nice, because colorEnum will be undefined - and you can then check for that specifically.
    – M Falanga
    Commented May 14, 2020 at 20:13
  • 11
    Pair this with a Object.values(Enum).indexOf(value) >= 0 check, to see that it's a valid value.
    – zahanm
    Commented Jul 28, 2020 at 7:16
170
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

4
  • 1
    I don't know why, but this solution does not work on on a const enum (using Typescript 3.8.3) Commented Apr 29, 2020 at 12:50
  • 13
    @Robin-Hoodie A bit late to the party (but for the benefit of other latecomers), but const enums are compiled out of the code completely, so the runtime code has no references to the enum keys, only their literal values. It therefore has no way to map those values back to the enum keys.
    – David G
    Commented Dec 7, 2020 at 11:42
  • 3
    This is the best answer! Commented Jun 10, 2021 at 15:49
  • 4
    Only answer that makes invalid values undefined
    – Sourabh
    Commented Aug 13, 2021 at 17:16
101

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.

3
  • 29
    One of the reasons I would want to use enums is to restrict to valid cases. In this example I could do const color = 'banana' as Color and it still parses fine, but the color is no longer valid. Commented Jun 24, 2020 at 16:25
  • @DanielWood it's a good remark, I added it to my answer earlier.
    – Nick N.
    Commented Feb 1, 2021 at 19:43
  • 2
    @DanielWood Good point! What is the correct solution to prevent what you're saying? Commented Nov 17, 2021 at 19:56
89

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
}

Playground


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
4
  • You can also cast to <keyof typeof Color>. Also "0" is wrong input too but will not return undefined, so check typeof mayBeColor==='number'
    – Quentin 2
    Commented Jul 20, 2018 at 18:15
  • @Quentin2 what about a numeric string? ie typeof '0' should be string Commented May 3, 2019 at 20:45
  • Note that this is not very safe. (<any>Color)["hasOwnProperty"] will work but not the way you expect.
    – Mat
    Commented Sep 25, 2020 at 6:52
  • @Mat, why? Why will it work but not in the expected way?
    – Hauns TM
    Commented Jun 15, 2021 at 11:17
41

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)

Playground sample

1
  • My enum value is of type number and doing it like EnumHere[stringVar] just says: Element implicitly has an 'any' type because index expression is not of type 'number'.ts(7015)
    – CyberMew
    Commented Apr 22, 2023 at 20:14
34

I got it working using the following code.

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

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.

1
  • Thank you! This is a pretty silly issue and hard to figure out what the problem is.Maybe Typescript should consider coming up with a better way of handling enums. Commented Jan 31, 2020 at 5:38
19

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.

1
  • 3
    why not just use typecast <string>colorName instead of "" + colorName?
    – SergeyT
    Commented Jun 8, 2017 at 13:01
18

I also ran into the same compiler error. Just a slight shorter variation of Sly_cardinal's approach.

var color: Color = Color[<string>colorId];
2
  • As an addition: In case you have a typescript enum filled by a javascript layer that serialized the enum as string (say for example Asp Web API via AngularJS) you can do myProp.color = Color[<string><any>myProp.color] Cheers
    – Victor
    Commented Sep 16, 2015 at 14:40
  • 1
    This must be the recognized answer. Commented Jan 21, 2016 at 22:27
15

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 to any, because with TypeScript 3.0+ (currently using TypeScript 3.5), the enumObj is resolved as unknown.

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");
2
  • 9
    Property 'toString' does not exist on type 'T[K]'.ts(2339)
    – abrkn
    Commented Jul 22, 2020 at 1:29
  • Maybe a bit more readable: const stringToEnumByValue = <T>(enumObj: Object, value: string): T | undefined => Object.values(enumObj).find((v) => v === value); and const stringToEnumByKey = <T>(enumObj: Object, value: string): T | undefined => Object.values(enumObj)[Object.keys(DeliveryState).indexOf(value)];
    – chrisski
    Commented Dec 8, 2021 at 11:33
10

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/

2
  • This answer is genious! Love it. Especially the way you make an enum out of the string. This can save you so much typing when testing enums or other cases. Commented Jul 17, 2019 at 16:06
  • Yes, I use this with Jest's each to test every single enum case with only one method
    – mikeb
    Commented Jul 18, 2019 at 17:32
10

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;
9

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
9

Almost all answers use unsafe casts (as or is). This one doesn't:


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")
8

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:

  1. We're explicitly declaring these as string-backed enum cases which allows us to instantiate them with strings, not some other unrelated numbers.

  2. We've added an option that may or may not exist on our server model: UNKNOWN. This can be handled as undefined 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
3
  • 11
    Unfortunately, this seems to be not correct or, at least, not generalizable. It works because your keys equal the strings they got assigned. If they, like in my case, differ, however, this does not work. In the words of the documentation: "Keep in mind that string enum members do not get a reverse mapping generated at all." Your code will compile to something like IssueType["REPS"]="REPS". If you had defined your enum a little different, say, REPS="reps" this would yield IssueType["REPS"]="reps" which would... Commented Dec 5, 2018 at 15:30
  • ...always return IssueType.UNKNOWN because there is no key reps in your enum. Too bad, I still found no working solution for this since my strings contain hyphens which makes them unusable as keys. Commented Dec 5, 2018 at 15:31
  • Finally, I found a solution in this answer by convincing the compiler that this was not a string enum. It might be worth editing this info into your own answer. Commented Dec 5, 2018 at 15:53
7

For Typescript >= 4 this code worked:

enum Color{
    Red, Green
}

// Conversion :
var green= "Green";
var color : Color = green as unknown as Color; 
2
  • this is not safe, use this approach if you are 100% sure that var green holds a value of enum Color. Commented Dec 13, 2022 at 6:42
  • This saved.. But I'm sure Typescript designers were smoking something while writing the specs 😁 Commented Mar 22 at 22:02
6

For TS 3.9.x

var color : Color = Color[green as unknown as keyof typeof Color];
1
  • Neat! It seems to work without the as unknown part too!
    – Tom
    Commented Feb 23, 2021 at 9:42
5

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;
    }
}
3
  • Anybody who has enum like me should put return enumType[property]; in a case when your enum item looks like Skills = "anyvalue"
    – kkost
    Commented Jul 12, 2019 at 23:09
  • @neustart47 could you please to ask the question? Commented Jul 14, 2019 at 13:50
  • it's not a question. I just mentioned some changes for anybody who is searching for the same case as I have. Your answer is correct.
    – kkost
    Commented Jul 14, 2019 at 23:53
5

In short, it is impossible but I hate forced assertion using as. This is what I recommend.

enum Color {
  Red,
  Green
}

Create a map but optimize the creation such that you don't do this often.

const reverse = new Map(Object.values(Color).map(item => [item.toString(), item]))

Now dump that function and just use the reverseMap to get the color enum.

const colorText: string = "Red"
const colorEnum: Color | undefined = reverse.get(colorText)

👆note the potential undefined, since you are converitng a text to an enum, you MUST verify it explicityly. This solution also allows you to supply a default by using

const colorWithFallback: Color = reverse.get("Kind of Yellow") || Color.Red
4

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:

enter image description here

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
4

Typescript 3.9 propsal

enum Color{ RED, GREEN }

const color = 'RED' as Color;

easy peasy... lemon squeezy!

4
  • 7
    this is unsafe, 'BANANA' as Color will also pass
    – dtech
    Commented Mar 4, 2021 at 9:55
  • 1
    I'm still getting downvoted, the question asked is: "How can I convert that value to an enum?" So this is an answer, may be not the perfect one, but in that case it's better to explicitly tell why it not. There are lots of solutions, probably most of them are good, depending on the situation.
    – funder7
    Commented Aug 3, 2021 at 14:57
  • 1
    if the question is "how do I exit a program" an answer is "throw an Error and don't catch it". However it's a bad answer. This is similar, this way completely removes the benefit a type system brings by manually overriding it. Imagine having to debug a system where somewhere someone used 'YELLOW' as Color, but in the meantime yellow was removed as a Color. Typescript is supposed to catch bugs like this, but won't if you manually override it.
    – dtech
    Commented Aug 12, 2021 at 13:57
  • 1
    I totally agree with you @dtech, probably my knowledge of TS doesn't go that far, I tought that the code above would have setup everything properly (types included). When I posted it, there wasn't any other simple solution, as far as I can remeber. Looking at this thread I can see a bit of confusion around the topic, it should be easier in my opinion. How would you answer to OP ?
    – funder7
    Commented Sep 14, 2021 at 15:00
4

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"

3

Try this

var color : Color = (Color as any)["Green];

That works fine for 3.5.3 version

3

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');
2
  • beautiful solution
    – IulianT
    Commented Jan 31, 2022 at 10:31
  • 1
    To note that this solution doesn't work with string enums. I.e. enum Color { red = 'R', green = 'G'} Color.parse('R') Commented Jul 15, 2022 at 10:49
2

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

2

Most of the answers provided do not offer broad support for Enums. Granted the OP requested to get the Enum only from a string value, but Enums also allow other values.

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;
}

This will work for all enums no matter how complex (or odd), so long they're not flagged.

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;
4
  • I think the question is how to string "MORE" convert to OddEnum.More, and genericValueToEnum does not solve this. If you call genericValueToEnum(Color, "Red"), it will return undefined. Commented Jan 20, 2023 at 1:09
  • MORE in my example is only the key. However, in the OP's example enum, Green is is both the key and value. Enums are intended to work on their value, rather than the key.
    – roydukkey
    Commented Jan 20, 2023 at 1:26
  • Yep, and the question is how to Key given as a string "Key" convert to the MyEnum.Key. I mean, your answer is not relevant at all. Commented Jan 20, 2023 at 1:41
  • > the question is how to Key given as a string "Key" < Hum... I'm not sure why you think so, given that "key" is mentioned no where by the OP. Anyway, I still stand by my position that needing working with Enum in that way is likely due to an architecting issue.
    – roydukkey
    Commented Jan 20, 2023 at 17:35
1

other variation can be

const green= "Green";

const color : Color = Color[green] as Color;
1
  • This generates TypeScript error: TS7015 (TS) Element implicitly has an 'any' type because index expression is not of type 'number' Commented Apr 25, 2023 at 10:23
1

I have created a simple EnumUtil, which may help you. The only thing I cannot eliminate is that we need to pass the enum two times. It validates the input on parsing.

Sample Usage


enum NamesEnum {
  Bill = 'Bill Hogwards',
  John = 'John Doe',
}

//the idea is to init the util for a given enum once for the whole project and reuse it
const namesEnumUtil = EnumUtil.forString<NamesEnum>(NamesEnum);
const value = namesEnumUtil.parse("Bill Hogwards"); //parse requires string as an input
const allValues = namesEnumUtil.getAllValues(); //returns string[]

const wrongValue = namesEnumUtil.parse("Value is not there"); //this line will throw Error

Typescript playground link

The Enum Util Class itself:

type EnumType = 'string' | 'number';

type GetResultType<T extends EnumType> = T extends 'string'
  ? string
  : T extends 'number'
  ? number
  : never;

class EnumUtil<TEnum, TEnumType extends EnumType> {
  private constructor(
    private readonly theEnum: any,
    private readonly enumType: TEnumType
  ) {}

  static forString<TEnum>(theEnum: any) {
    return new EnumUtil<TEnum, 'string'>(theEnum, 'string');
  }

  static forInt<TEnum>(theEnum: any) {
    return new EnumUtil<TEnum, 'number'>(theEnum, 'number');
  }

  getAllValues(): Array<GetResultType<TEnumType>> {
    const result = Object.keys(this.theEnum).map(
      (key) => this.theEnum[key] as GetResultType<TEnumType>
    );
    if (this.enumType === 'string') {
      return result;
    } else if (this.enumType === 'number') {
      return result
        .map((v) => parseInt(v as string))
        .filter((v) => !isNaN(v)) as Array<GetResultType<TEnumType>>;
    }

    throw new Error(
      `enum type "${this.enumType}" is not supported in EnumUtil.getAllValues`
    );
  }

  parse(value: GetResultType<TEnumType>) {
    //protect the input
    const allPossibelValues = this.getAllValues();

    if (!allPossibelValues.includes(value)) {
      throw new Error(
        `Cannot parse enum value "${value}". It should be one of ${allPossibelValues.join(
          ','
        )} `
      );
    }
    return value as unknown as TEnum;
  }
}
1
  • Useful utility! A simple change converts arrays too: parseArray(vv: GetResultType<TEnumType>[]) { return vv.map(e=> this.parse(e) ); } and works with @Transform Commented Feb 11 at 17:25

Not the answer you're looking for? Browse other questions tagged or ask your own question.