如何在转换单参数函数为展开形式时保留高阶函数的泛型
如何将接收单个参数的函数转换为可展开形式的高阶函数时保留其泛型特性?
问题场景
我们需要实现一个高阶函数,将接收单个参数的函数转换为支持接收多个参数或参数数组的形式,同时保留原函数的泛型特性。以下是初始尝试及遇到的问题:
const implementation = (() => {}) as any; type Spread1 = <A, R>( fn: (arg: A) => R ) => { (arg: A, ...args: A[]): R[]; (args: A[]): R[]; }; const spreaded1: Spread1 = implementation; const result1 = spreaded1(<G extends string>(g: G) => g); // 实际得到的类型: // { // (arg: unknown, ...args: unknown[]): unknown[]; // (args: unknown[]): unknown[]; // } // 期望得到的类型: // { // <G extends string>(arg: G, ...args: G[]): G[] // <G extends string>(arg: G[]): G[] // }
尝试1:条件类型方案
尝试用条件类型提取原函数的参数和返回值类型,但依然无法保留泛型:
type Spread2<F> = F extends (arg: infer A) => infer R ? { (arg: A, ...args: A[]): R[]; (args: A[]): R[] } : never; const spreaded2 = <F>(fn: F): Spread2<F> => implementation; const result2 = spreaded2(<G>(g: G) => g);
尝试2:重新分配泛型方案
尝试为返回的函数重新声明泛型,但出现类型不兼容错误:
type Spread3 = <A, R>( fn: (arg: A) => R ) => { <A2 extends A, R2 extends R>(arg: A2, ...args: R2[]): R2[]; <A2 extends A, R2 extends R>(args: A2[]): R2[]; }; const spreaded3: Spread3 = implementation; const result3_1 = spreaded3(<G>(g: G) => g); const result3_2 = spreaded3(<G extends string>(g: G) => g); // 错误信息: // Argument of type // '<G extends string>(g: G) => G' // is not assignable to parameter of type // '(arg: unknown) => string'. // // Types of parameters 'g' and 'arg' are incompatible. // Type 'unknown' is not assignable to type 'string'.ts(2345)
可行解决方案
通过在原函数类型中添加一个无关的泛型参数(<_>),让TypeScript正确保留原函数的泛型特性:
const implementation = (() => {}) as any; type Spread = <A, R>( fn: <_>(arg: A) => R ) => { <A0 extends A, R0 extends R>(arg: A0, ...args: A0[]): R0[]; <A0 extends A, R0 extends R>(args: A0[]): R0[]; }; const spreaded1: Spread = implementation; const result1 = spreaded1(<G extends string>(g: G) => g); // 此时result1的类型符合预期,保留了原函数的泛型约束
内容的提问来源于stack exchange,提问作者Janek Eilts
相关产品推荐
相关产品推荐

