繁体   English   中英

Typescript:a 类型的参数不能分配给 b 类型的参数

[英]Typescript: argument of type a is not assignable to parameter of type b

我的接口定义如下:

type Shape = | Triangle | Rectangle;
interface Triangle {...}
interface Rectangle {...}

function foobar(func: (shape: Shape) => void) {...}

function test() {
    const fb = (rect: Rectangle) => {...};
    foobar(fb); // giving me error: argument of type a is not assignable to parameter of type b...
}

但如果我做下面的事情,它工作正常:

function computeArea(shape: Shape) {
  .....
}


const triangle: Triangle = {...};
const rect: Rectangle = {...}

computeArea(triangle);

我想知道为什么类型 Rectangle 与方法 computeArea 中的 Shape 兼容,而它与方法 test 中的 Shape 不兼容。 解决方法测试问题的最佳实践是什么?

我在这里添加了迷你复制代码: https://playcode.io/934375/

正如您的示例中设计的那样, RectangleShape union 的成员。

容易理解的情况是computeArea function,它可以处理任何Shape ,因此用Rectangle调用它就可以了。

但是,与直觉相反, foobar function 则相反,它需要一个处理Shape的回调,但是用一个接受Rectangle的回调调用它会产生错误......

正如我们所说,回调 arguments 是逆变的,即这里应该foobar期望回调处理Rectangle ,我们可以完成foobar(computeArea)

如果我们想象foobar在内部构建一个任意Shape (然后可能是一个Triangle ),并尝试使用给定的回调处理它,则可能更容易理解为什么它在问题示例中不起作用: fb不合适在那种情况下(因为它不能处理Triangle ,只能处理Rectangle )。

而如果foobar说它需要一个处理Rectangle的回调,这意味着它在内部只会使用Rectangle参数执行该回调。 因此,可以处理任何Shape (因此包括Rectangle )的回调,例如computeArea ,本来就很好。


在您的复制代码中,如果我理解正确, foobar function 实际上是:

function getShapeArea(shape: Shape, computeArea: (shape: Shape) => number) {
  const area = computeArea(shape);
  return area;
}

当我们尝试调用它时,TypeScript 会出错:

getShapeArea(circle, computeCircleArea);

...其中circle是一个Circle (也是Shape union 的成员),而computeCircleArea是一个 function ,它接受一个Circle并返回一个number

这里转译的 JavaScript 代码在运行时运行良好(尽管有 TS 错误消息)。

但是如果回调参数是协变的,我们也可以使用 function 作为:

getShapeArea(triangle, computeCircleArea);

...在这种情况下,代码很可能会引发异常(没有半径的三角形)。

如果我们有一个适用于任何ShapecomputeArea回调,那么它就会起作用(说明回调参数的逆变),因为无论getShapeArea function 的实际 shape 第一个参数是什么,它都可以由那个假设的computeArea回调处理。

在您的精确情况下,您可能希望向 TS 提供更多提示,即getShapeArea的 2 个 arguments 是相关的(回调只需要处理相同的形状,而不是任意形状)。 通常使用泛型:

function getShapeArea<S extends Shape>(shape: S, computeArea: (shape: S) => number) {
  const area = computeArea(shape);
  return area;
}

有了这个, getShapeArea(circle, computeCircleArea)不再是 TS 错误!

getShapeArea(triangle, computeCircleArea)更明显是一个错误(因为computeCircleArea无法处理三角形)。


根据您的具体情况,您甚至可以通过自动检测形状并相应地使用适当的计算 function (不必每次都将其指定为第二个参数)来进一步改进getShapeArea function ,使用 类型缩小

function getShapeArea(shape: Shape) {
  // Using the "in" operator narrowing
  // https://www.typescriptlang.org/docs/handbook/2/narrowing.html#the-in-operator-narrowing
  if ("radius" in shape) {
    // If "Circle" type is the only "Shape" type with a "radius" property,
    // then TS automatically guesses that "shape" is a Circle
    return computeCircleArea(shape); // Okay because TS now knows that "shape" is a Circle
  } else if ("width" in shape && "length" in shape) {
    // Similarly, if "Rectangle" is the only "Shape" with both "width" and "length",
    // then TS guesses that shape is a Rectangle
    return computeRectangleArea(shape); // Okay because TS now knows that "shape" is a Rectangle
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM