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;