首页 > 解决方案 > 如何修复“类型的参数......不可分配给类型的参数......”错误?

问题描述

给定

abstract class A {
  constructor() {
    this.initialize()
  }

  initialize<T extends {
    [t in keyof this]?: boolean
  }>(todos?: T) {
    // Do something
  }
}

class B extends A {
  initialize() {
    super.initialize({ // <-- it will throw error starting on this open bracket
        test: false
    })
  }

  test() {
    return 'test'
  }
}

为什么上面的代码抛出错误说明 { test: false }不可分配给{[t in keyof this]?: boolean}?虽然它显然是。

'test' 是 B 的键之一,对吧?并且keyof this会参考B的键,对吧?

标签: typescript

解决方案


正确的抽象类定义

首先,你在“作弊”。

abstract class A {
  constructor() {
    this.initialize()
    //   ~~~~~~~~~~ this `initialize` is from subclass
  }

  // different from this one here
  initialize<T extends {
    [t in keyof this]?: boolean
  }>(todos?: T) {
    // Do something
  }
}

initialize您只是通过给它们相同的名称和兼容的函数签名(可选todos参数)来欺骗 TS 认为您正在调用相同的方法。

你想要做的应该写成:

abstract class A {
  constructor() {
    this.initialize()
  }

  // fix 1: delcare abstract method
  abstract initialize(): void

  // fix 2: rename, mark as protected
  protected _initialize<T extends {
    [t in keyof this]?: boolean
  // fix 3: todos probably isn't optional
  }>(todos: T) {
    // Do something
  }
}

class B extends A {
  initialize() {
    super._initialize({
        test: false
    })
  }

  test() {
    return 'test'
  }
}

子类和基类之间的连接

这是一种方式。子类知道基类,但基类没有义务知道所有扩展自身的子类。因为每个子类都可以实现不同的东西,所以基类应该如何知道所有这些信息?

这就是抽象类的全部意义所在。您在基类上声明抽象方法或属性,这实际上是基类建立的契约。哪个子类想扩展我?先签合同!遵守我的条件!

回到你的情况,为什么_initialize应该A知道任何事情B?也可能有CD扩展A仅从推断keyof子类是不可能的this

所以调用的时候需要super._initialize说明子类是什么。this这是要求,而不是 TS 的限制。

abstract class A {
  constructor() {
    this.initialize()
  }

  abstract initialize(): void

  // pass subclass type as generic param B
  protected _initialize<B>(todos: {
    [t in keyof B]: boolean
  }) {
    // Do something
  }
}

class B extends A {
  initialize() {
    super._initialize<B>({
      test: true,  // <-- this is correct now
      foobar: true // <-- this triggers error report
    })
  }

  test() {
    return 'test'
  }
}

你可以在 JS 中做很多骇人听闻的事情,但不要将 JS 与 TS 混淆。TS 主要是关于最佳实践,当你打破它们时,你会遇到各种怪癖和错误。你的情况不是 TS 的限制,那是 TS 试图说服你回到正确的轨道。


推荐阅读