TypeScript Class 使用联合类型字段实现接口导致错误 (2322)

TypeScript Class Implements Interface with Union Type Field results in Error (2322)

我不明白为什么会出现错误 2322。

请检查以下代码片段:

interface Fish {
  alive: string | boolean;
}

class FishClass implements Fish {
  alive  = 'Yes'

  constructor() {
    // Type 'boolean' is not assignable to type 'string'.(2322)
    this.alive = true; 
  }
}

接口为string定义联合类型|布尔值。

为什么当我将两种可能的类型之一分配给实现接口的 class 内的字段时,类型是固定的?

尽管大多数人的期望是,class 成员并不是 contextually typed 声明要实现或扩展的类型。 class 上的 extendsimplements 声明对 class 实例的类型没有任何影响。相反,它只是在事后对 superclass 或接口执行 class 的 check。因此,如果您的 class 未能实现或扩展您声明的内容,您将收到编译器警告。但是当涉及到结果类型时,它与完全删除 extendsimplements 子句一样。不,没有人喜欢这个。

参见microsoft/TypeScript#3667 for the original request to fix this, and there was an attempt at microsoft/TypeScript#6118 but it was eventually abandoned due to some problems with existing libraries; see this comment. There are several open issues about this, including microsoft/TypeScript#32082;如果你想看到这个问题得到解决,你可能想去那个问题并给它一个,但现在我们只需要接受这样一个事实,即 class Foo implements Bar {/*...*/}Foo 的类型具有相同的含义正如 class Foo {/*...*/} 所做的那样。


在您的情况下,这意味着 class FishClass implements Fish {/*...*/} 在成员推断方式方面与 class FishClass {/*...*/} 完全相同。由于 alive 属性 是用 string"Yes" 初始化的,编译器将其推断为 string 类型。稍后当您将 true 分配给它时,这是一个错误,因为您不能将 boolean 分配给 string。请注意,string 可分配给 string | boolean,因此对 FishClass implements Fish 的检查成功;您可以缩小子类型的属性。

目前处理此问题的正确方法是手动注释 field 所需的类型:

class FishClass implements Fish {
   alive: string | boolean = 'Yes'; // okay

   constructor() {
      this.alive = true; // okay
   }
}

Playground link to code