Append Argument
问题描述
实现一个泛型 AppendArgument<Fn, A>,对于给定的函数类型 Fn,以及一个任意类型 A,返回一个新的函数 G。G 拥有 Fn 的所有参数并在末尾追加类型为 A 的参数。
type Fn = (a: number, b: string) => number
type Result = AppendArgument<Fn, boolean>
// 期望是 (a: number, b: string, x: boolean) => number
本挑战来自于 @maciejsikora 在 Dev.io 上的文章
// ============= Test Cases =============
import type { Equal, Expect } from './test-utils'
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>>,
// @ts-expect-error
AppendArgument<unknown, undefined>
]
// ============= Your Code Here =============
type AppendArgument<Fn extends (...args: any[]) => any, A> = Fn extends (...args: infer R) => infer T
? (...args: [...R, A]) => T
: never
这道题难点在于,Fn 如何约束,如果使用 Fn extends Function 范围太大,所以应该缩小泛型的范围 (...args: any[]) => any 只包括普通函数类型,不包括构造函数等。其次,如何得到函数参数的值类型和返回值类型,这里使用了关键字 infer 将值类型和返回值类型保存在了泛型 R 和泛型 T 中。并构建一个新的函数返回。使用extends关键字检查Fn是否是一个泛型函数,即它的参数类型是否是infer R。如果是,那么Fn的参数类型就是[...R, A]。然后,使用infer关键字创建一个新的类型参数T,它的类型是Fn的返回类型。最后,使用never类型断言来确保Fn不是泛型函数,或者Fn的参数类型不是infer R。