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

问题描述 投票:1回答:1

考虑到我们有一个表示三种不同字符串值之一的并集类型。

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错误?

javascript typescript switch-statement union-types
1个回答
0
投票

您可能会误解的是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"的值,因此,在重新分配变量之前,我们将对待变量[ C0]作为类型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"

"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;

© www.soinside.com 2019 - 2024. All rights reserved.