我試圖將泛型類型實現到類中,但事實證明,我不了解泛型可以做什么,不能做什么。
希望有人能幫我!
我想要什么
我已經創建了一個類,該類應該注冊任務并稍后按id運行它們。taskObject
由taskId: string
和回調函數callback: <R>(...args: any) => R
組成。實例化類時將注冊任務。
其想法是,我有關于每個單獨任務的回調函數返回類型的類型信息。
因此,taskId
是一個文字類型,callback
應該是一個任意函數,我想推斷它的返回類型,并使這個返回類型在我的類中可訪問。
The code
type Task<T extends string, V extends <R>(...args: any) => R> = {
taskId: T
callback: V // <-- I would like this resolved
}
class Taskmanager<T extends string, V extends <R>(...args: any) => R> {
private callbacks = new Map<T, V>();
public constructor(tasks: Task<T,V>[]) {
tasks.forEach(task => {
this.callbacks.set(task.taskId, task.callback);
});
}
public runCallback(taskId: T) {
return this.callbacks.get(taskId)?();
}
}
這是第一步→ 我有所有注冊taskIds
的類型提示。
const tm = new Taskmanager([
{
taskId: "hello",
callback: () => "world" // <-- TS error: Type 'string' is not assignable to type 'R'
},
{
taskId: "circle",
callback: () => 360 // <-- TS error: Type 'number' is not assignable to type 'R'
}
]);
// I'm now getting type hints for `taskId` → "hello" | "circle"
const resHello = tm.runCallback("hello"); // <-- I would like: typeof resHello === "string" --> true
const resCircle = tm.runCallback("hello"); // <-- I would like: typeof resCircle === "number" --> true
這是TS游樂場鏈接
這給了我預期的結果,即我只能在hello
和circle
之間選擇作為runCallback
中的參數。但是,在類構造函數中聲明回調函數時,我會遇到TS錯誤。
// TS Error for taskId: "hello"
Type 'string' is not assignable to type 'R'.
'R' could be instantiated with an arbitrary type which could be unrelated to 'string'.
有沒有一種方法可以推斷每個回調函數的返回類型?這可能嗎?
我確實在StackOverflow上查看了其他幾個問題和答案,但不幸的是,到目前為止,我無法提取任何有用的內容。
如果這個問題很愚蠢,我深表歉意。TS有時真的讓我抓狂。。。
非常感謝。🙌
由于您計劃在完全沒有參數的情況下調用這些回調,因此您希望將
V
保持為() => any
,而不是<R>(...args: any) => R
(這是通用的,甚至不可能正確實現)。事實上,您甚至可能只想使用R
,然后使callback
為() => R
類型,但我將進行最小的更改:現在,由于您計劃向
Taskmanager
構造函數傳遞一個條目數組,我們需要使該類在該數組的元素的類型T
中具有泛型,如下所示:請注意,我將其設為
const
類型參數,因為我們希望編譯器推斷taskId
屬性的字符串文字類型,而不僅僅是string
。還請注意,我將
callbacks
設為Map<string, ()=>any>
,而不是更具體的T
。這是因為Map<K, V>
沒有跟蹤關系特定鍵和特定值所需的類型。Map<K, V>
就像一個字典,其中所有值都是V
類型。如果您在Map
中需要其他東西,您可以嘗試為其編寫自己的類型,如Typescript:我如何在ES6映射中基于對象鍵/值類型進行條目,但這可能有些過頭了。我們將使用() => any
,然后在實現runCallback()
時要小心。它看起來是這樣的:
這是一個相當復雜的調用簽名,但有必要這樣做,因為
T
是Task<string, ()=>any>
的子類型的并集。因此,我們希望將K
約束到T
的taskId
屬性的并集,然后返回類型要求我們從并集中Extract
獲取其taskId
屬性為K
類型的成員,獲取其{callback
屬性,并獲取其中的ReturnType
。實現需要使用non-null斷言運算符來說服編譯器get()
不會返回undefined
。我們只需要小心,因為編譯器將返回類型視為any
,它可以分配給任何對象。我們本可以寫this.callbacks.get(taskId+"oops")!();
,它也會編譯。無論如何,讓我們測試一下:
看起來不錯。構造函數調用被接受,并且
tm
的類型編碼了足夠的關于構造函數參數的信息,以計算tm.runCallback("hello")
的正確類型,即string
。游樂場代碼鏈接