[英]How do you do an exhaustive switch case in Typescript with string enums that refer to other string enums
I have a string enum that is a subset of the values from another string enum.我有一个字符串枚举,它是另一个字符串枚举值的子集。 I am trying to do an exhaustive switch case across the former.
我正在尝试对前者进行详尽的切换案例。
This is a contrived example of the construct I am attempting to represent in Typescript这是我试图在 Typescript 中表示的构造的一个人为示例
const enum Color {
BLUE = 'Blue',
YELLOW = 'Yellow',
RED = 'Red'
}
const enum SupportedColor {
BLUE = Color.BLUE,
YELLOW = Color.YELLOW
}
function doSomething(colorFromOutsideInput: string): boolean {
const supportedColor: SupportedColor = (colorFromOutsideInput as unknown) as SupportedColor;
switch (supportedColor) {
case SupportedColor.BLUE:
return true;
case SupportedColor.YELLOW:
return true;
default:
return invalidColor(supportedColor);
}
}
function invalidColor(supportedColor: never): never {
throw new Error();
}
In this code, the default in the switch case fails compilation with在这段代码中, switch case 中的默认值编译失败
Argument of type 'SupportedColor' is not assignable to parameter of type 'never'.
If I were to do this switch case across the Color
enum instead of SupportedColor
enum, it would work.如果我要在
Color
枚举而不是SupportedColor
枚举中执行此切换案例,它将起作用。
Based on reading in the typescript docs, I see no evidence of why the compiler treats the two enums differently because they evaluate to the same strings.基于对打字稿文档的阅读,我没有看到编译器为什么会以不同的方式对待这两个枚举的证据,因为它们评估为相同的字符串。
I'm going to minimize the example a bit but the issues raised are the same.我会尽量减少这个例子,但提出的问题是一样的。
Enums where each enum value is of an explicit string literal or numeric literal type act like union types , which can be narrowed via type guards .每个枚举值都是显式字符串文字或数字文字类型的枚举的行为类似于联合类型,可以通过类型保护来缩小范围。 This sort of enum is called a union enum :
这种枚举称为联合枚举:
// union enum
const enum SupportedColor {
BLUE = "Blue",
YELLOW = "Yellow"
}
// no error here
function doSomething(supportedColor: SupportedColor): boolean {
switch (supportedColor) {
case SupportedColor.BLUE:
supportedColor // SupportedColor.BLUE
return true;
case SupportedColor.YELLOW:
supportedColor // SupportedColor.YELLOW
return true;
}
//supportedColor // never
//~~~~~~~~~~~~~ <-- unreachable code
}
In the above code, the compiler recognizes that the switch
statement is exhaustive because supportedColor
of type SupportedColor
can be narrowed by control flow anlaysis.在上面的代码时,编译器识别出该
switch
语句是穷举因为supportedColor
类型的SupportedColor
可以通过控制流anlaysis变窄。 The type SupportedColor
is equivalent to the union SupportedColor.BLUE | SupportedColor.YELLOW
SupportedColor
类型等价于联合SupportedColor.BLUE | SupportedColor.YELLOW
SupportedColor.BLUE | SupportedColor.YELLOW
. SupportedColor.BLUE | SupportedColor.YELLOW
。 SupportedColor.BLUE | SupportedColor.YELLOW
。
So inside the various case
s, supportedColor
is narrowed in turn to the type SupportedColor.BLUE
and SupportedColor.YELLOW
.因此,在各种
case
, supportedColor
依次缩小为SupportedColor.BLUE
和SupportedColor.YELLOW
类型。 After the switch
statement is over, the compiler knows that supportedColor
is of type never
, since both SupportedColor.BLUE
and SupportedColor.YELLOW
have been filtered out of the union. switch
语句结束后,编译器知道supportedColor
的类型为never
,因为SupportedColor.BLUE
和SupportedColor.YELLOW
已从联合中过滤掉。 If you uncomment the code after the switch
block, the compiler will even complain that it is unreachable in TS3.7+.如果在
switch
块之后取消注释代码,编译器甚至会抱怨它在 TS3.7+ 中无法访问。
Therefore all code paths return a value and the compiler does not complain.因此所有代码路径都返回一个值,编译器不会抱怨。
Now consider what happens when you change the enum values to be calculated instead of literals:现在考虑当您更改要计算的枚举值而不是文字时会发生什么:
const enum Color {
BLUE = 'Blue',
YELLOW = 'Yellow',
RED = 'Red'
}
// calculated enum
const enum SupportedColor {
BLUE = Color.BLUE,
YELLOW = Color.YELLOW
}
function doSomething(supportedColor: SupportedColor): boolean { // error!
// --------------------------------------------> ~~~~~~~
// function lacks ending return statement
switch (supportedColor) {
case SupportedColor.BLUE:
supportedColor // SupportedColor
return true;
case SupportedColor.YELLOW:
supportedColor // SupportedColor
return true;
}
supportedColor // SupportedColor
}
Here the enum type SupportedColor
is no longer considered to be a union type.这里枚举类型
SupportedColor
不再被视为联合类型。 Calculated enums are not union enums .计算的枚举不是联合枚举。 And therefore the compiler has no way to narrow or filter the type of
supportedColor
in or after the switch
statement.因此,编译器无法缩小或过滤
switch
语句中或之后的supportedColor
类型。 The type stays SupportedColor
the whole way through.类型在整个过程中保持
SupportedColor
。 And the switch
statement isn't seen to be exhaustive, and the compiler complains that the function doesn't always return a value.并且
switch
语句并不详尽,编译器抱怨该函数并不总是返回一个值。
So that explains what's going on, and the documentation link for union enums does say that union enums and calculated enums are different.这解释了发生了什么,并且联合枚举的文档链接确实说联合枚举和计算枚举是不同的。 So it's documented.
所以它被记录在案。 But what isn't explained in the documentation is why calculated enums are not treated as unions.
但是文档中没有解释的是为什么计算的枚举不被视为联合。 That is: it's working as designed, but does anyone actually prefer it to be this way, or is it just a design limitation?
也就是说:它按设计工作,但是否有人真的更喜欢这样,还是只是设计限制?
The closest thing I can find to a canonical answer is the GitHub issue microsoft/TypeScript#22709 referencing some meeting notes inside microsoft/TypeScript#26241 .我能找到的最接近规范答案的是 GitHub 问题microsoft/TypeScript#22709引用了microsoft/TypeScript#26241 中的一些会议笔记。 These notes are:
这些笔记是:
- There are actually many kinds of enums under the hood
引擎盖下实际上有很多种枚举
- Union enums only get created when only bare literals (or nothing) are initializers
仅当只有裸文字(或没有)是初始值设定项时,才会创建联合枚举
- These kinds of enums behave differently
这些类型的枚举行为不同
- You might want one or the other and this is how you choose
您可能想要其中一个,这就是您的选择
- Working As Intended;
按预期工作; would be a Breaking Change
将是一个突破性的变化
So the TS team says that non-union enums are actually sometimes desirable and that making a change here would break existing code which relies on the current behavior.因此 TS 团队表示,非联合枚举实际上有时是可取的,并且在此处进行更改会破坏依赖于当前行为的现有代码。 It's not particularly satisfying of an answer since it doesn't describe the use cases in which people want non-union enums, but it's an answer nonetheless: this is intentional.
它不是特别令人满意的答案,因为它没有描述人们想要非联合枚举的用例,但它仍然是一个答案:这是故意的。 If you want union enums, use literals and not calculated values.
如果您想要联合枚举,请使用文字而不是计算值。
Okay, hope that helps;好的,希望有帮助; good luck!
祝你好运!
I'm not sure that I understand the intention of the example, but consider changing the signature of invalidColor
to receive type unknown
:我不确定我是否理解该示例的意图,但请考虑更改
invalidColor
的签名以接收类型unknown
:
function invalidColor(supportedColor: unknown): never {
throw new Error();
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.