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

标签 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;
  case 'dog':
    console.log('bark');
    break;
}

此代码将导致 TypeScript 错误:Type '"bird"' is not comparable to type '"dog"'.ts(2678) (与猫类比)。但是,如果我对变量 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 错误?

最佳答案

您可能会误解的是 TypeScript 2.0 及更高版本有一个名为 control-flow based type analysis 的功能。 ,在 microsoft/TypeScript#8010 中实现.此功能的效果之一是

An assignment (including an initializer in a declaration) of a value of type S to a variable of type T changes the type of that variable to T narrowed by S in the code path that follows the assignment. [...] The type T narrowed by S is computed as follows: [...] If T is a union type, the result is the union of each constituent type in T to which S is assignable.



这意味着声明
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' case 是不可能的,因为你没有重新分配 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;

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

Playground link to code

关于javascript - 联合类型的变量导致 switch 语句中的错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59988669/

相关文章:

javascript - NextJS _app.tsx Component 和 pageProps 应该是什么 TypeScript 类型?

javascript - 从 yii 中的不同表中搜索

angular - 在 typescript 类中使用数组

html - Angular 5 : Spinner is not working perfectly on page load

javascript - switch 语句适合多少种情况

c# - 在 switch/case c# 中使用 const 字符串变量或仅使用字符串的最佳/常见做法?

javascript - 为什么 JavaScript 保留关键字允许作为变量名?

javascript - jquery在ajax中调用函数时this指针作用域

javascript - 如何以编程方式对 Material UI DataGrid 列进行排序?

c++ - 具有相同名称的静态变量的开关案例的单独范围