簡體   English   中英

應用函數名稱和參數的函數類型

[英]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

好的,希望有幫助; 祝你好運!

Playground 鏈接到代碼

暫無
暫無

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

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