如何为多泛型参数类型的可变参数函数实现正确类型推断?
多泛型参数可变参数函数的类型推断解决方案
单泛型参数场景
首先定义单泛型类型:
type O1<T> = { a: T, b: NoInfer<T> };
接着声明两个不同的单参数泛型函数:
function f1T<T>(arg: O1<T>) {} function f1O<T, G extends O1<T>>(arg: G) {}
调用时可见,f1T能正确校验类型,而f1O无法完成正确推断:
f1T/*<number>*/({ a: 0, b: ''}); // 报错:string不能赋值给number(符合预期) f1O/*<unknown, { a: number, b: string }>*/({ a: 0, b: ''}); // 被允许(不符合预期)
基于f1T的思路,可实现正确工作的可变参数函数:
function fmT<Ts extends unknown[]>(...args: { [K in keyof Ts]: O1<Ts[K]> }) {} fmT({ a: 0, b: 1 }, { a: '0', b: 2 }); // 报错:number不能赋值给string(符合预期)
双泛型参数场景
单参数场景可直接沿用相同模型:
type O4<T, U> = { a: T, b: NoInfer<T>, c: U, d: NoInfer<U> }; function f1TU<T, U>(arg: O4<T, U>) {} f1TU({ a: 0, b: 1, c: '', d: 2 }); // 报错:number不能赋值给string(符合预期)
但在可变参数场景下,尝试多种写法均无法解决类型推断问题:
- 朴素写法:报错
Type 'K' cannot be used to index type 'Us'
function fmTU_naive<Ts extends unknown[], Us extends unknown[]>(...args: { [K in keyof Ts]: O4<Ts[K], Us[K]> }) {}
- 键交集写法:破坏元组映射,报错
A rest parameter must be of an array type
function fmTU_inter<Ts extends unknown[], Us extends unknown[]>(...args: { [K in (keyof Ts & keyof Us)]: O4<Ts[K], Us[K]> }) {}
- 条件类型写法:破坏推断逻辑,错误允许不符合要求的参数
type AsNumber<S> = S extends `${infer N extends number}` ? N : never; function fmTU_cond<Ts extends unknown[], Us extends unknown[]>(...args: { [K in keyof Ts]: AsNumber<K> extends keyof Us ? O4<Ts[K], Us[AsNumber<K>]> : never; }) {} fmTU_cond/*<[number], unknown[]>*/({ a: 0, b: 1, c: '', d: 2 }); // 被允许(不符合预期)
- 分组泛型参数写法:同样破坏推断逻辑
function fmTU_index<const TUs extends [T: unknown, U: unknown][]>(...args: { [K in keyof TUs]: O4<TUs[K][0], TUs[K][1]>; }) {} fmTU_index/*<[T: unknown, U: unknown][]>*/({ a: 0, b: 1, c: '', d: 2 }); // 被允许(不符合预期)
最终解决方案
通过辅助类型从O4<unknown, unknown>中推断出合法的O4类型,实现可用的可变参数函数:
const TS_ERROR = Symbol(); type TSError<Msg extends string> = { [TS_ERROR]: Msg }; type InferO4<O extends O4<unknown, unknown>> = O extends O4<unknown, unknown> ? O['a'] extends infer T ? O['b'] extends T ? O['c'] extends infer U ? O['d'] extends U ? O4<T, U> : TSError<'Type O[\'d\'] is not assignable to type O[\'c\']'> : never : TSError<'Type O[\'b\'] is not assignable to type O[\'a\']'> : never : TSError<'Type O is not assignable to type O4<unknown, unknown>' >; type InferO4s<Os extends O4<unknown, unknown>[]> = { [K in keyof Os]: InferO4<Os[K]> }; function fmO_helper< Os extends O4<unknown, unknown>[], Is = InferO4s<Os> >( ...args: Os & InferO4s<Os> ) {}; fmO_helper( { a: 0, b: 1, c: '0', d: '1'}, { a: Symbol(), b: 0, c: 0, d: 1} ); // Is: [O4<number, string>, TSError<"Type O['b'] is not assignable to type O['a']">](符合预期)
内容的提问来源于stack exchange,提问作者ghostinpeace
相关产品推荐
相关产品推荐

