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 takeFn
as the first argument,A
as the second, and will produce function type G which will be the same asFn
but with appended argumentA
as 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
,期待再相見!