簡體   English   中英

如何將參數綁定到通用 function

[英]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 類型操作。 理想情況下,有一種方法可以說“使用Usertypeof retrieve中指定T ”,然后您只需在綁定參數之前執行此操作。 但是沒有任何合理的方法可以做到這一點。 也許如果該語言支持microsoft/TypeScript#1213中要求的更高種類的類型microsoft/TypeScript#17574中要求的通用值,那么就有一種方法可以干凈地做到這一點。


最少的臨時解決方法是顯式寫出類型“在typeof retrieve中指定TUser ”, 將變量注釋為該類型並將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可以用作該類型。

Playground 代碼鏈接

它現在合並到 main 中,尚未實現是最后一個版本;)。

=> https://github.com/microsoft/TypeScript/pull/47607

暫無
暫無

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

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