[英]Types for function that applys name of function and arguments
我正在嘗試以正確的方式鍵入函數,該函數為該函數應用函數名稱和參數。 之后應用它並返回結果。 這里的代碼:
const sum = (a: number, b: number) => a + b
const concat = (a: string, b: string, c: string) => a + b + c
const funs = {
sum,
concat
}
type Keys = 'sum' | 'concat'
type Args<T> = T extends (...args: infer R) => any ? R : never
type Sum = Args<typeof sum>
type Concat = Args<typeof concat>
function apply<K extends Keys>(funKey: K, ...args: Args<typeof funs[K]>) {
// here I get the error 'An argument for 'a' was not provided.'
return funs[funKey](...args)
}
const test1 = apply('sum', 1, 2)
const test2 = apply('concat', 'str1', 'str2', 'str3' )
在函數apply
我收到錯誤“未提供 'a' 的參數。”。 我怎樣才能擺脫這個錯誤?
編譯器將無法理解這是類型安全的,因為它通常不能很好地推理依賴於尚未指定的泛型類型參數的類型的可分配性。 現有的 GitHub 問題microsoft/TypeScript#24085描述了這種情況。
事實上,有可能(但不太可能)在您的函數中, K
可能被推斷為Keys
本身,而不是"sum"
或"concat"
。 如果你這樣做:
const oops = apply(Math.random() < 0.5 ? "sum" : "concat", "a", "b", "c"); // oopsie
console.log(oops); // 50% chance of "abc", 50% chance of "ab"
然后你會看到編譯器在技術上是正確的,你所做的不是類型安全的。 您想告訴編譯器K
將恰好是Keys
的成員之一,但您不能。 有關允許這樣做的功能建議,請參閱microsoft/TypeScript#27808 。
無論如何,編譯器無法將funKey
參數和args
rest 參數視為具有相關類型。 即使可以,它在保持相關性方面也不是很好,請參閱microsoft/TypeScript#30581了解更多相關信息。
它也無法理解計算返回類型,因此您必須對其進行注釋。 您可以ReturnType<F>
使用ReturnType<F>
實用程序類型。 請注意,還有一個Parameters<F>
實用程序類型,您可以使用它來代替自己編寫Args<F>
。
因此,歸根結底,您只需要告訴編譯器您所做的是類型安全的(您不會在某些聯合類型的funKey
上調用apply()
,對吧?),因為它無法驗證它。 要做到這一點,您需要諸如類型斷言之類的東西。 這里最容易使用的是 good old any
:
type Funs = typeof funs;
function apply<K extends Keys>(funKey: K, ...args: Parameters<Funs[K]>): ReturnType<Funs[K]> {
return (funs[funKey] as any)(...args);
}
這將允許你做一些瘋狂的事情,比如return (funs[funKey] as any)(true)
,所以你應該小心。 稍微更類型安全但更復雜的是將funs[funKey]
表示為一個函數,該函數以某種方式接受每個函數期望的參數,並返回兩種返回類型。 像這樣:
type WidenFunc<T> = ((x: T) => void) extends ((x: (...args: infer A) => infer R) => any) ?
(...args: A) => R : never;
function apply<K extends Keys>(funKey: K, ...args: Parameters<Funs[K]>): ReturnType<Funs[K]> {
return (funs[funKey] as WidenFunc<Funs[Keys]>)(...args);
}
這里WidenFunc<Funs[Keys]>
是(...args: [number, number] | [string, string, string]) => number & string
。 這是一種無意義的函數類型,但至少它會抱怨如果你傳遞一個像(true)
而不是(...args)
。
無論如何,其中任何一個都應該有效:
const test1 = apply('sum', 1, 2) // number
const test2 = apply('concat', 'str1', 'str2', 'str3') // string
好的,希望有幫助; 祝你好運!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.