Javascript union类型的变量导致switch语句出错

Javascript union类型的变量导致switch语句出错,javascript,typescript,switch-statement,union-types,Javascript,Typescript,Switch Statement,Union Types,假设我们有一个表示三个不同字符串值之一的联合类型 type Animal = 'bird' | 'cat' | 'dog'; 现在,我想创造一只狗,并检查它是什么样的动物,以创造正确的噪音,它执行 let oscar: Animal = 'dog'; switch (oscar) { case 'bird': console.log('tweet'); break; case 'cat': console.log('meow'); break; c

假设我们有一个表示三个不同字符串值之一的联合类型

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错误:
类型“bird”与类型“dog”不可比较。ts(2678)
(与cat类似)。但是,如果我在变量
oscar
上使用显式类型转换,则它可以正常工作:

switch (oscar as Animal) {
  case 'bird':
    ...
  case 'cat':
    ...
  case 'dog':
    ...
}
如果我使用显式值表示
oscar
,请解释为什么前两个switch语句失败

如果我将Oscar声明为常量,我可以理解这个错误:
constoscar='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错误?

您可能误解的是TypeScript 2.0及以上版本有一个名为的功能,在中实现。此功能的一个效果是

S
类型的值赋值给
T
类型的变量(包括声明中的初始值设定项),会在赋值后的代码路径中将该变量的类型更改为
T
,并缩小
S
。[…]由
S
缩小的类型
T
计算如下:[…]如果
T
是联合类型,则结果是
T
中的每个组成类型的联合,而
S
是可分配的

这意味着声明

let oscar: Animal = 'dog';
解释为:“变量
oscar
的类型为
Animal
,是一种联合类型。它被分配了一个字符串文本类型
“dog”
,因此在重新分配之前,我们将把变量
oscar
视为
Animal
缩小了
“dog”
,即
“dog”“

因此,在您的
开关
/
案例
语句中:

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

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

如果你不想让编译器意识到奥斯卡肯定是一只狗,只知道它是一只动物(比如说,在你编写真正可能成为动物的任何成员的代码之前,它是一个占位符),你可以在作业本身中使用:

let oscar: Animal = 'dog' as Animal;
现在,所有其他代码都将编译无误。您甚至可以忘记注释,因为它对您没有帮助:

let oscar = 'dog' as Animal;
好吧,希望这会有帮助;祝你好运


回答得很好,非常感谢您对窄联轴节类型的详细解释,这是一个我以前不知道但对我来说很有意义的概念。
let oscar = 'dog' as Animal;