[英]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.