[英]How to bind a parameter to a generic function
我有一個通用的 function retrieve
:
function retrieve<T>(endpoint: string, id: string, /* etc */): T {}
我想定義一個 function,例如retrieveUser
,它將綁定第一個參數並指定T
。
我嘗試使用Function.prototype.bind()
:
const retrieveUser = retrieve.bind(/* this */ undefined, "/user");
但這阻止我指定T
。
然后我嘗試使用 rest 參數:
type User = { username: string, /* etc */ };
const retrieveUser = (...args) => retrieve<User>("/user", ...args);
這正是我想要的,但 Typescript 抱怨:
error TS7019: Rest parameter 'args' implicitly has an 'any[]' type.
error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
是否可以使 Typescript 從retrieve
function 推斷args
類型? 有沒有更好的方法來實現我的目標?
不幸的是,TypeScript 不直接支持您想要在此處執行的那種高階通用 function 類型操作。 理想情況下,有一種方法可以說“使用User
在typeof retrieve
中指定T
”,然后您只需在綁定參數之前執行此操作。 但是沒有任何合理的方法可以做到這一點。 也許如果該語言支持microsoft/TypeScript#1213中要求的更高種類的類型或microsoft/TypeScript#17574中要求的通用值,那么就有一種方法可以干凈地做到這一點。
最少的臨時解決方法是顯式寫出類型“在typeof retrieve
中指定T
與User
”, 將變量注釋為該類型並將retrieve
分配給它:
const userRetrieve: (endpoint: string, id: string, /* etc */) => User = retrieve; // okay
編譯器對此很滿意,這意味着即使沒有簡單的方法可以在類型系統中以編程方式用User
指定T
,但它確實明白,如果可以的話, retrieve
可以分配給您獲得的類型。 這意味着這里有類型安全; 如果你輸入錯誤(比如(endpoint: number, id: string) => User
)你會得到一個編譯器錯誤。
無論如何,現在您可以將參數綁定到userRetrieve
:
const retrieveUser = userRetrieve.bind(undefined, "/user");
// const retrieveUser: (id: string) => User
如果retrieve
的參數不是通用的,您可以使用Parameters<T>
實用程序類型來獲取參數類型列表並為您節省一些擊鍵:
const userRetrieve: (...args: Parameters<typeof retrieve>) => User = retrieve; // okay
const retrieveUser = userRetrieve.bind(undefined, "/user");
// const retrieveUser: (id: string) => User
在這兩種情況下,如果您不關心類型安全性而不關心權宜之計,您可以將類型注釋變量替換為類型斷言:
const retrieveUser =
(retrieve as (...args: Parameters<typeof retrieve>) => User).
bind(undefined, "/user");
這是我能做到的。
但是,如果參數確實依賴於泛型類型參數, Parameters<T>
將失去對任何類型參數的跟蹤:
declare function retrieve<T>(endpoint: string, id: string, somethingGenric: T): T;
const retrieveUser =
(retrieve as (...args: Parameters<typeof retrieve>) => User).
bind(undefined, "/user");
// const retrieveUser: (id: string, somethingGenric: unknown) => User 😢
糟糕,這是unknown
的,而不是User
。 為此,我們必須自己寫出參數並正確插入類型參數:
const retrieveUser =
(retrieve as (endpoint: string, id: string, somethingGenric: User) => User).
bind(undefined, "/user");
// const retrieveUser: (id: string, somethingGenric: User) => User
有一些方法可以濫用對高階通用 function 推理的有限支持,以使編譯器為您插入T
,但這實際上是巫術魔法。 我不會解釋它,因為我已經在這個答案中這樣做了,我不知道我是否可以用一種對人們有意義的方式來解釋它。 不過,這里是:
class GenTypeMaker<T> {
getGenType!: <A extends any[], R>(cb: (...a: A) => R) => () => (...a: A) => R;
genType = this.getGenType(null! as typeof retrieve)<T>()
}
type Retrieve<T> = GenTypeMaker<T>['genType']
// type Retrieve<T> = (endpoint: string, id: string, somethingGenric: T) => T
您會看到,當您指定T
時,編譯器已將Retrieve<T>
的類型推斷為與retrieve
的 function 類型相同。 所以Retrieve<User>
是你想要的類型:
const retrieveUser =
(retrieve as Retrieve<User>).
bind(/*this*/ undefined, "/user");
// const retrieveUser: (id: string, somethingGenric: User) => User
這不是很酷嗎? 但不是我推薦的任何東西。 在這種情況下,您應該自己寫出類型並注釋或斷言retrieve
可以用作該類型。
它現在合並到 main 中,尚未實現是最后一個版本;)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.