I have a const enum
like the following:
const enum Color {
Red = "red",
Green = "green",
Blue = "blue"
}
I also have some string (say, colorString
), coming from outside. Its value is supposed to be either "red"
, or "green"
, or "blue"
, but it's not for sure. I'd like to validate it and throw in case of the invalid value.
For 'conversion', I could do just:
const color: Color = colorString as Color;
This 'converts' string to enum, but doesn't validate it. If constString === "purple"
there will be no error at runtime. For validation, I could do:
function stringToColor(colorString: string): Color {
switch (colorString) {
case Color.Red:
case Color.Green:
case Color.Blue:
return colorString; // no need to do 'as', TS knows that colorString is valid Color enum value
default:
throw new Error(`Invalid color ${colorString}.`);
}
}
This 'converts' string to enum and validates it, but it's kinda verbose and error-prone. E.g., if I add new value to the Color
enum, I'll have to add new case
statement to the switch
in the stringToColor
function in order for my code to operate properly.
Afaik, const enums exist only at compile time, and are completely erased to plain strings in runtime. So there is no any real conversion. Plus, I can't do Object.values(Color)
, because Color
is const enum
and doesn't exist at runtime. What I actually need is:
- to check if the string has one of designated (valid) values - at runtime;
- to tell TypeScript that string can be represented as enum - at compile time.
Another solution is to keep all valid values in an object like this:
const colors: { readonly [color in Color]: color } = {
[Color.Red]: Color.Red,
[Color.Green]: Color.Green,
[Color.Blue]: Color.Blue,
};
The good part here is that this object must contain property for every enum value (unlike Color[]
). If I add a new value to enum, but forget to add it to the colors
object, if will fail at compile time. So it's less error-prone, than just writing a dozen of case
statements. Then I could do:
function stringToColor(colorString: string): Color | undefined {
return Object.values(colors).find((value) => colorString === value);
}
It returns undefined
in case of invalid value, which is acceptable.
However, I wonder if there are less verbose and more elegant solutions out there.