For given function type
Fn, and any typeA(any in this context means we don't restrict the type, and I don't have in mind any type 😉) create a generic type which will takeFnas the first argument,Aas the second, and will produce function type G which will be the same asFnbut with appended argumentAas a last one.
對於給定的函數類型 Fn,以及任何類型 A(這裡的“任何”是指我們不限制這個類型,我也沒有具體想到哪種類型 😉),創建一個泛型類型,它將接受 Fn 作為第一個參數,A 作為第二個參數,並生成函數類型 G。G 將與 Fn 相同,但會在最後追加一個 A 類型的參數。
type Fn = (a: number, b: string) => number
type Result = AppendArgument<Fn, boolean>
// expected be (a: number, b: string, x: boolean) => number
接下來,你的任務是讓下面的type cases測試通過:
type Case1 = AppendArgument<(a: number, b: string) => number, boolean>
type Result1 = (a: number, b: string, x: boolean) => number
type Case2 = AppendArgument<() => void, undefined>
type Result2 = (x: undefined) => void
type cases = [
Expect<Equal<Case1, Result1>>,
Expect<Equal<Case2, Result2>>,
]
從以下幾個方向來思考:
類型推斷 (Type Inference):使用 TypeScript 的 infer 關鍵字來推斷函數的參數列表和返回值。在這裡,我們需要推斷出 Fn 函數的參數類型 P 以及返回值類型 R。
條件型別 (Conditional Types):這是 TypeScript 的一個功能,可以根據特定條件來確定類型。在這裡,我們使用 Fn extends (...arg: infer P) => infer R 來檢查 Fn 是否為一個函數,並且提取它的參數類型 P 和返回值 G。
參數展開 (Parameter Spread):利用 TypeScript 的展開運算符 ...,可以將函數的參數類型擴展成一個數組。在這裡,我們將推斷出的參數類型 P 展開,並在末尾添加類型 A 作為新的參數。
解法:
type AppendArgument<Fn extends (...arg: any[]) => unknown, A> = Fn extends (
...arg: infer P
) => infer G
? (...arg: [...P, A]) => G
: never
細節分析:
類型限制 (Strict Type Constraints) type AppendArgument<Fn extends (...arg: any[]) => unknown, A>`` 這種寫法更嚴謹,因為它要求 Fn必須是一個函數類型(…arg: any[]) => unknown`。這樣可以在有人意外傳遞非函數類型時,讓 TypeScript 提示錯誤,
條件型別 (Conditional Types):Fn extends (...arg: infer P) => infer G 用來檢查 Fn 是否是一個函數,並提取它的參數類型 P 和返回值類型 G。
參數推斷 (Parameter Inference):利用 infer P 提取函數的參數類型,infer G 提取返回值類型。這使我們能夠動態地操作 Fn 的參數。
參數組合 (Parameter Concatenation):[...P, A] 是用來將原始的參數類型展開,並在末尾追加新的參數類型 A。這樣就生成了帶有新參數的函數類型。我們使用了 元組 (Tuple) 的展開語法。P 是一個元組,這裡的 A 是一個新的元素。這個語法的含義是 arg 參數將會是一個包含了 P 中所有元素,並在最後附加一個 A 的元組。
例如,如果 P 是 [number, string],而 A 是 boolean,那麼 ...arg 的類型會是 [number, string, boolean]。
函數返回值保持不變:函數的返回值類型 G 保持不變,這確保了新生成的函數仍然符合原始的輸出預期。
這樣,我們就能順利通過測試啦 🎉 😭 🎉
本次介紹了 Append Argument 的實作,下一關會挑戰 Permutation,期待再相見!