簡體   English   中英

打字稿泛型:返回數組類型以按位置與映射類型匹配輸入數組

[英]Typescript generics: Return array type to match input array by position with mapped types

我有一個函數可以按數據類型(例如 U8、U16、F32、String 等)從緩沖區中獲取值。 我試圖弄清楚如何鍵入函數,例如,如果我傳入['u8','u16','u16','string']推斷的返回類型將是[number,number,number,string] 我發現了一個類似的問題並遵循相同的模式,但無法得到我需要的結果。

這是代碼的相關部分:

type BytesTypeMap = {
  U8: number,
  U16: number,
  U32: number,
  String: string,
  Text: string,
}
export type BytesType = keyof BytesTypeMap;
type BytesTypeMapped<T> = T extends BytesType ?  BytesTypeMap[T] : never;
type BytesTypeArray<T extends BytesType[]> = {
  [K in keyof T]: BytesTypeMapped<T[K]>;
}

export class MessageBuffer {

  read<T extends BytesType[]>(types:T):BytesTypeArray<T>{
    return types.map(type=>{
      const method = `read${type}` as const;
      return this[method](); // Typescript error: Type '(string | number)[]' is not assignable to type 'BytesTypeArray<T>'
    });
  }

  // ... methods matching those created with the template string above...
}

// GOAL
// ... create an instance of the class, etc.
messageBufferInstance.read(["U8","U16","String"]);
// Inferred response type should be [number,number,string]
// but is instead (number|string)[]

編譯器不夠聰明,無法理解map()如何在read()的實現中將一種類型的通用元組轉換為映射類型的元組。 Array.prototype.map()方法標准庫類型

interface Array<T> {
  map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
}

它只映射數組到數組而不是元組到元組......特別是不是元組的BytesType -to-tuples-of- BytesTypeMapped 而且這樣的簽名對於map()這個特定調用是如此特定,以至於即使試圖提出它並將其合並Array接口中也將是白費力氣。

相反,我建議接受編譯器無法在這里驗證類型安全的任務,通過使用類型斷言來明確告訴編譯器您對正確的類型負責:

read<T extends BytesType[]>(types: [...T]): BytesTypeArray<T> {
  return types.map((type: BytesType) => {
    const method = `read${type}` as const; // assuming we're using TS4.1+
    return this[method]();
  }) as BytesTypeArray<T>;
}

請注意我們如何將其as BytesTypeArray<T>返回。 它足夠接近(string | number)[]

旁白:在 TS4.1 引入microsoft/TypeScript#40707之前,我不認為​`read${type}` as const會起作用。 這很快就會出來,所以我會離開它。

這樣就可以處理函數的實現方面。 現在到調用方:


另一部分是得到

const resp = messageBufferInstance.read(["U8", "U16", "String"]);

被推斷為[number, number, string]而不是(string | number)[] 我們可以通過更改read()方法簽名來給編譯器一個提示,如果可能的話, T應該是一個元組,而不是在調用read()時擴展為數組,從而實現這一點。

有不同的方法可以做到這一點,在 TS 4.0 引入可變元組類型之前,你必須這樣做(參見microsoft/TypeScript#27179

// read<T extends BytesType[] | [BytesType]>(types: T): BytesTypeArray<T> {

其中T的約束具有元組類型,但現在您可以這樣做:

read<T extends BytesType[]>(types: [...T]): BytesTypeArray<T> {

其中types參數是從T傳播的可變參數元組。 無論哪種方式都應該從調用者的角度工作:

const resp = messageBufferInstance.read(["U8", "U16", "String"]);
resp[0].toFixed(); // okay
resp[2].toUpperCase(); // okay

我更喜歡[...T]方法,因為它在實現方面更容易。


好的,所以調用者和實現都應該按預期工作。

Playground 鏈接到代碼

暫無
暫無

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

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