联合类型变量导致switch语句错误

考虑一下,我们有一个联合类型来表示三个不同的字符串值之一。

type Animal = 'bird' | 'cat' | 'dog';

现在,我想创建一只狗,并检查它是哪种动物,以产生正确的噪音。

let oscar: Animal = 'dog';

switch (oscar) {
  case 'bird':
    console.log('tweet');
    break;
  case 'cat':
    console.log('meow');
    break;
  case 'dog':
    console.log('bark');
    break;
}

此代码将导致TypeScript错误:Type '"bird"' is not comparable to type '"dog"'.ts(2678)(带有cat的模拟)。但是,如果我对变量oscar使用显式类型强制转换,则它可以正常工作:

switch (oscar as Animal) {
  case 'bird':
    ...
  case 'cat':
    ...
  case 'dog':
    ...
}

能否请我解释一下,如果我为oscar使用一个显式值,为什么前两个switch语句失败?

如果我将Oscar声明为常量:const oscar = 'dog';,我会理解该错误,因为在那种情况下,它将始终是一条狗,别无其他。但是,请想象一下,如果一个巫师会执行某种咒语,奥斯卡可能会变成猫:

let oscar: Animal = 'dog';

while(true) {
  switch (oscar) {
  case 'bird':
    ...
  case 'cat':
    ...
  case 'dog':
    console.log('bark');

    // here comes the wizard
    if(wizard.performsspell('makeOscarBecomeACat')) {
      oscar = 'cat';  // that should be valid,because oscar is of type Animal
    }

    break;
  }
}

我是否误解了变量oscar的分配,还是仅仅是TypeScript错误?

linda881633 回答:联合类型变量导致switch语句错误

您可能会误解的是TypeScript 2.0及更高版本具有称为control-flow based type analysis的功能,该功能已在microsoft/TypeScript#8010中实现。

将类型S的值T的赋值(包括声明中的初始化程序)分配给变量T的类型,将变量的类型更改为 S分配后的代码路径中的T 。 [...]缩小了S T类型的计算如下:[...]如果T是联合类型,则结果为S可分配给let oscar: Animal = 'dog'; 的每种组成类型的并集。

这意味着声明

oscar

解释为:“变量Animal的类型为"dog",是联合类型。它已被分配了字符串文字类型oscar的值,因此直到重新分配它为止,我们会将变量Animal视为类型为 "dog"并缩小了"dog" 的类型,即switch

因此在您的case / case 'bird': // error! // ~~~~~~ <-- Type '"bird"' is not comparable to type '"dog"' 语句中:

"bird"

您会收到有关尝试将字符串文字"dog"与字符串文字'bird'进行比较的错误。编译器知道oscar是不可能的,因为您没有将'bird'重新分配给与wizard兼容的东西。

即使在您的switch情况下,编译器也理解,当到达case / oscar语句时,"cat"只能是"dog"或{{ 1}},而不是"bird"

case 'bird': // error! 
//   ~~~~~~ <-- Type '"bird"' is not comparable to type '"cat" | "dog"'

这可能都是好消息;编译器正在捕获永远不会发生的情况。在许多情况下,这些都是真正的错误。

如果您不希望编译器意识到oscar绝对是"dog",而只知道它是Animal(例如,占位符,直到您编写真正使它成为代码的代码可能是Animal的任何成员,则可以在作业本身中使用type assertion

let oscar: Animal = 'dog' as Animal;

现在,所有其他代码将正确编译。您甚至可以忘记该注释,因为它无法帮助您

let oscar = 'dog' as Animal;

好的,希望能有所帮助;祝你好运!

Playground link to code

本文链接:https://www.f2er.com/2683543.html

大家都在问