簡體   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