打字稿类型 a 不可分配给类型 b

Typescript type a is not assignable to type b

故事

我正在使用 konva library 和 Typescript,它是 HTML5 Canvas JavaScript 框架。

这是我从 javascript 改成 typescript 的简单教程代码。

import Konva from "konva";

const stage = new Konva.Stage({
  container: 'container',
  width: window.innerWidth,
  height: window.innerHeight
});

const layer = new Konva.Layer();
stage.add(layer);

const box = new Konva.Rect({
  x: 50,
  y: 50,
  width: 100,
  height: 50,
  fill: '#00D2FF',
  stroke: 'black',
  strockWidth: 4,
  draggable: true
});

layer.add(box);

但是我的 Visual Studio 代码显示如下错误消息。

Argument of type 'Rect' is not assignable to parameter of type 'Group | Shape<ShapeConfig>'.
  Type 'Rect' is not assignable to type 'Shape<ShapeConfig>'.
    Types of property 'hitFunc' are incompatible.
      Type 'GetSet<(ctx: Context, shape: Rect) => void, Rect>' is not assignable to type 'GetSet<(ctx: Context, shape: Shape<ShapeConfig>) => void, Shape<ShapeConfig>>'.
        Type '(ctx: Context, shape: Rect) => void' is not assignable to type '(ctx: Context, shape: Shape<ShapeConfig>) => void'.
          Types of parameters 'shape' and 'shape' are incompatible.
            Type 'Shape<ShapeConfig>' is missing the following properties from type 'Rect': _sceneFunc, cornerRadiusts(2345)

class ShapeShape.d.ts 中,RectRect.d.ts 中。 我可以看到 Rect 扩展了 Shape!但是 VSC 显示错误。

如果我将 Rect.d.ts 中的两个 属性 添加到 Shape class, 错误消失了

export declare class Shape<Config extends ShapeConfig = ShapeConfig> extends Node<Config> {
    _sceneFunc(context: any): void; // this was in Rect class
    cornerRadius: GetSet<number, this>; // this was in Rect class
    ...
    ...

问题

我知道 layer.add() 函数只接受 Shape 类型。但正如您所知,Typescript 允许像下面这样向下转换。

class Base {
  baseProperty: string = "aaaa";
}
class A extends Base {
  aProperty: number = 1;
}
var a: A = new A();
function test(arg: Base) {
  console.log(arg.baseProperty);
}
test(a); // => No Error

我不明白为什么 layer.add() 不能接受 Rect 类型,即使它扩展了 Shape 类型。我错过了什么?

我需要你的建议。 谢谢。

不幸的是,库的定义显然没有用 strictFunctionTypes 进行测试。

如果未激活此选项,函数将双变相关,但在 strictFunctionTypes 下,函数将根据参数类型逆变相关。这意味着:

class Animal { private x:undefined }
class Dog extends Animal { private d: undefined }

type EventHandler<E extends Animal> = (event: E) => void

let o: EventHandler<Animal> = (o: Dog) => { } // fails under strictFunctionTyes

你得到的错误是在 hitFunc 上(如果它在 sceneFunc 上是固定的)这些是采用第二个参数 this 的函数,实际上 [=18] =].当我们尝试在 Rect 中分配带有第二个参数的 hitFunc 时,这会触发错误,该参数也输入为 this 但实际上是 Rect。您不能将具有派生参数类型的函数分配给接受任何基类型参数的函数签名。

有多种可能的解决方案:

  1. 使用类型断言

    layer.add(box as unknown as Shape<ShapeConfig>);
    
  2. 禁用strictFunctionTypes(tsconfig中的strictFunctionTypes: false错误消失)

  3. 提交一个 pull request 以明确输入以使 hitFuncsceneFuncstrictFunctionTypes: true 下表现出双变事件,如 所述。在这种情况下,这将起作用并保留功能:

    export type ShapeConfigHanlder<TTarget> = {
        bivarianceHack(ctx: Context, shape: TTarget): void
    }['bivarianceHack']
    export interface Shape extends NodeConfig {
        ...
        hitFunc: GetSet<ShapeConfigHanlder<this>, this>;
        sceneFunc: GetSet<ShapeConfigHanlder<this>, this>;
    }