![](/img/trans.png)
[英]Typescript error 'argument of type is not assignable to parameter of type'
[英]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/
正如您的示例中设计的那样, Rectangle
是Shape
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);
...在这种情况下,代码很可能会引发异常(没有半径的三角形)。
如果我们有一个适用于任何Shape
的computeArea
回调,那么它就会起作用(说明回调参数的逆变),因为无论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.