简体   繁体   English

使用打字稿中的验证将字符串转换为常量枚举

[英]Convert string to const enum with validation in typescript

I have a const enum like the following:我有一个像下面这样的const enum

const enum Color {
    Red = "red",
    Green = "green",
    Blue = "blue"
}

I also have some string (say, colorString ), coming from outside.我还有一些字符串(比如colorString ),来自外面。 Its value is supposed to be either "red" , or "green" , or "blue" , but it's not for sure.它的值应该是"red""green""blue" ,但不确定。 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.如果constString === "purple"运行时不会出错。 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.这将“转换”字符串为枚举并验证它,但它有点冗长且容易出错。 Eg, 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.例如,如果我向Color枚举添加新值,则必须向stringToColor函数中的switch添加新的case语句,以便我的代码正常运行。

Afaik, const enums exist only at compile time, and are completely erased to plain strings in runtime. Afaik,const 枚举仅在编译时存在,并在运行时完全擦除为纯字符串。 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.另外,我不能做Object.values(Color) ,因为Colorconst enum并且在运行时不存在。 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.在编译时告诉 TypeScript 字符串可以表示为枚举。

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[] ).这里的好处是这个对象必须包含每个枚举值的属性(与Color[]不同)。 If I add a new value to enum, but forget to add it to the colors object, if will fail at compile time.如果我向 enum 添加一个新值,但忘记将它添加到colors对象,则 if 将在编译时失败。 So it's less error-prone, than just writing a dozen of case statements.因此,与仅编写十几个case语句相比,它更不容易出错。 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.如果值无效,它返回undefined ,这是可以接受的。

However, I wonder if there are less verbose and more elegant solutions out there.但是,我想知道是否有更简洁、更优雅的解决方案。

If you're using Color enum somewhere else in your program and want to stick with using it then unfortunately there's no elegant solution for what you're trying to do.如果您在程序中的其他地方使用Color enum 并想坚持使用它,那么不幸的是,对于您想要做的事情没有优雅的解决方案。 take a look at here .看看这里

But, there is one almost elegant way to achieve what you want to achieve, which is to use an object like below:但是,有一种几乎优雅的方法可以实现您想要实现的目标,即使用如下所示的对象:

const Color = {
    Red: "red",
    Green: "green",
    Blue: "blue"
} as const


const func = (color: typeof Color[keyof typeof Color]) => {
  console.log(color);
}

func("blue") // okay!
func("purple") // Argument of type '"purple"' is not assignable to parameter of type '"red" | "green" | "blue"'

The downside is that you can't use Color as a type like enums, for that you have to use typeof Color .缺点是您不能将Color用作枚举类型,因为您必须使用typeof Color

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM