如何修复 "Property has no initializer and is not definitely assigned in the constructor" 错误?

How to fix "Property has no initializer and is not definitely assigned in the constructor" error?

我在使用这些 class 时遇到了问题。我想使用 class B 独有的方法 doSomething() 而不是每次都进行类型转换,但是当我指定 属性 a 类型为 B,它告诉我它没有在构造函数中赋值,这有点假,因为父构造函数进行了赋值。

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()
  }
}

我做错了什么?

Playground

问题在于提议添加 class field declarations to JavaScript has different semantics from what you might expect and what TypeScript designers expected when they added them to TypeScript. It turns out that in JavaScript, class field declarations will be initialized via Object.defineProperty() 而不是通过赋值,并且所有这些没有初始化器的声明字段将被初始化为 undefined。所以最终你可以期待像你这样的代码生成 JavaScript 将 a 设置为 undefined 在 subclass 即使你的意图只是缩小基础 class。布莱奇

因此,在 TypeScript 3.7 中,a --useDefineForClassFields flag was added, along with the declare property modifier。如果您使用 --useDefineForClassFields,编译器将输出符合 class 字段的预期 Object.defineProperty() 语义的代码:

如果您 run your code as-is with that flag,您将在运行时看到问题:

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

解决方案是使用 declare 属性 修饰符来缩小 subclass 属性 而不发出任何相应的 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 link to code