0

我对这些课程有疑问。我想使用doSomething()class 独有的方法B而不每次都进行类型转换,但是当我将 property 指定a为 typeB时,它​​告诉我它没有在构造函数中赋值,这在父构造函数中进行赋值时有些错误。

class A {

}

class B extends A {
  doSomething() { }
}

class One {
  constructor(protected a: A){  }
}

class Two extends One {
  protected a: B // Property 'a' has no initializer and is not definitely assigned in the constructor.

  constructor(){
    super(new B());
    // If I enter "this.a = new B();" here then the error disappears, but the code is redundant.
  }

  doStuff() {
    this.a.doSomething()
  }
}

我究竟做错了什么?

操场

4

1 回答 1

4

问题在于,向 JavaScript 添加类字段声明的提议与您可能期望的语义以及 TypeScript 设计人员在将它们添加到 TypeScript 时所期望的语义不同。事实证明,在 JavaScript 中,类字段声明将通过Object.defineProperty()而不是通过赋值来初始化,并且所有没有初始化器的声明字段都将用undefined. 因此,最终您可以期望像您这样的代码生成在子类中设置a为的 JavaScript,undefined即使您的意图只是缩小基类的类型。布莱奇。

因此,在 TypeScript 3.7 中,添加了一个--useDefineForClassFields标志以及declare属性修饰符。如果使用--useDefineForClassFields,编译器将输出符合Object.defineProperty()类字段预期语义的代码:

如果您使用该标志按原样运行代码,您将在运行时看到问题:

new Two().doStuff()
// [ERR]: "Executed JavaScript Failed:" 
// [ERR]: this.a is undefined 

解决方案是使用declare属性修饰符来缩小子类属性,而不发出任何相应的Object.defineProperty()代码:

class Two extends One {
  declare protected a: B // okay

  constructor() {
    super(new B());
  }

  doStuff() {
    this.a.doSomething()
  }
}

new Two().doStuff(); // okay now

Playground 代码链接

于 2021-03-09T21:29:06.477 回答