簡體   English   中英

來自接口實現的Typescript泛型推理

[英]Typescript generic inference from interface implementation

我試圖從傳入的參數的泛型推斷方法的返回類型。但是,參數是來自泛型接口的實現,所以我假設typescript推理將從參數的基礎確定類型。

示例代碼:

interface ICommand<T> {}

class GetSomethingByIdCommand implements ICommand<string> {
  constructor(public readonly id: string) {}
}

class CommandBus implements ICommandBus {
  execute<T>(command: ICommand<T>): T {
    return null as any // ignore this, this will be called through an interface eitherway
  }
}

const bus = new CommandBus()
// badResult is {}
let badResult = bus.execute(new GetSomethingByIdCommand('1'))

// goodResult is string
let goodResult = bus.execute<string>(new GetSomethingByIdCommand('1'))

我想做的是第一次execute調用並使用打字稿推斷出正確的返回值,在這種情況下,這是基於GetSomethingByIdCommand實現的string

我試過玩條件類型,但不確定這是一個解決方案或如何應用它。

你的問題是ICommand<T>在結構上並不依賴於T (如@CRice的評論中所述)。

不建議這樣做 (⬅鏈接到一個TypeScript常見問題解答條目,詳細說明一個與這個案例幾乎完全相同的案例,因此這與我們可能會到達的官方單詞一樣接近)

TypeScript的類型系統(大部分)是結構的,而不是名義上的:當且僅當它們具有相同的形狀(例如,具有相同的屬性)時,兩種類型是相同的,並且它們與它們是否具有相同的名稱無關。 如果ICommand<T>結構上不依賴於T ,沒有它的性能有什么關系T ,然后ICommand<string>相同的類型 ICommand<number> ,這是相同的類型ICommand<ICommand<boolean>> ,與ICommand<{}>類型相同。 是的,這些都是不同的名稱 ,但類型系統不是名義上的,所以這並不重要。

在這種情況下,您不能依賴類型推斷來工作。 當你調用execute() ,編譯器會嘗試在ICommand<T>推斷出T的類型,但是沒有什么可以推斷它。 因此它最終默認為空類型{}

解決這個問題的方法是使ICommand<T>結構上以某種方式依賴於T ,並確保實現ICommand<Something>任何類型都正確地執行。 給出示例代碼的一種方法是:

interface ICommand<T> { 
  id: T;
}

因此ICommand<T>必須具有類型為Tid屬性。 幸運的是GetSomethingByIdCommand確實有一個類型為stringid屬性,這是implements ICommand<string>所要求的,所以編譯得很好。

而且,重要的是,您想要的推斷確實發生了:

// goodResult is inferred as string even without manually specifying T
let goodResult = bus.execute(new GetSomethingByIdCommand('1'))

好的,希望有所幫助; 祝好運!

如果具體類型在傳遞給ICommandBus.execute()之前被強制轉換為其通用等價物,那么Typescript似乎能夠正確地推斷出類型:

let command: ICommand<string> = new GetSomethingByIdCommand('1')
let badResult = bus.execute(command)

要么:

let badResult = bus.execute(new GetSomethingByIdCommand('1') as ICommand<string>)

這不是一個優雅的解決方案,但它的工作原理。 明顯的打字稿泛型不是很完整。

TS無法以您希望的方式推斷方法正在實現的接口。

這里發生的是當您使用以下實例化新類時:

new GetSomethingByIdCommand('1') 

實例化新類的結果是一個對象。 因此, execute<T>將返回一個對象而不是您期望的字符串。

執行函數返回結果后,您需要進行類型檢查。

在對象與字符串的情況下,您可以只進行類型檢查。

const bus = new CommandBus()
const busResult = bus.execute(new GetSomethingByIdCommand('1'));
if(typeof busResult === 'string') { 
    ....
}

這可以在運行時很好地工作,當typescript被編譯為純JS。

對於對象或數組(也是對象:D),您將使用類型保護。

類型保護試圖將項目轉換為某種東西並檢查屬性是否存在並推斷出使用了哪種模型。

interface A {
  id: string;
  name: string;
}

interface B {
  id: string;
  value: number;
}

function isA(item: A | B): item is A {
  return (<A>item).name ? true : false;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM