[英]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]
方法,因為它在實現方面更容易。
好的,所以調用者和實現都應該按預期工作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.