无需泛型,基于参数判别TypeScript函数返回类型
解决方案:判别式元组实现泛型多态函数的返回类型匹配问题
问题核心在于TypeScript无法自动将控制流分支中的返回值类型与PolymorphicFn泛型定义的返回类型PrimitiveOf<TUPLE[number]>建立关联。以下是两种可行的解决思路:
方案一:优化类型守卫+类型断言
先将参数类型守卫调整为精确匹配目标元组类型,再通过类型断言让TypeScript确认返回值符合泛型约束:
type PrimitiveOf<A extends string | number> = A extends string ? string : A extends number ? number : never; type PolymorphicFn<TUPLES extends (string | number)[]> = <TUPLE extends TUPLES>( ...args: TUPLE ) => PrimitiveOf<TUPLE[number]>; // 精确匹配[string, string]元组的类型守卫 const isStringTuple = (tuple: unknown[]): tuple is [string, string] => tuple.length === 2 && typeof tuple[0] === 'string' && typeof tuple[1] === 'string'; // 精确匹配[number, number]元组的类型守卫 const isNumberTuple = (tuple: unknown[]): tuple is [number, number] => tuple.length === 2 && typeof tuple[0] === 'number' && typeof tuple[1] === 'number'; const add: PolymorphicFn<[string, string] | [number, number]> = (...tuple) => { if (isStringTuple(tuple)) { const [a, b] = tuple; return `${a}${b}` as PrimitiveOf<typeof tuple[number]>; } if (isNumberTuple(tuple)) { const [a, b] = tuple; return a + b as PrimitiveOf<typeof tuple[number]>; } // 兜底处理(理论上不会触发,因参数已被约束为指定元组) throw new Error('Invalid arguments'); }; const num = add(1, 2); // 类型推断为number const str = add('1', '2'); // 类型推断为string
方案二:重构泛型函数类型定义
换一种方式定义PolymorphicFn,让返回类型与参数元组的关联更明确,消除TypeScript的推断盲区:
type PrimitiveOf<A extends string | number> = A extends string ? string : A extends number ? number : never; // 针对每个元组类型单独生成函数签名,再合并为联合类型 type PolymorphicFn<T extends readonly (readonly (string | number)[])[]> = T[number] extends infer U ? U extends readonly [infer A, infer B] ? A extends string ? B extends string ? (...args: U) => string : never : A extends number ? B extends number ? (...args: U) => number : never : never : never : never; // 定义合法的参数元组集合 type ValidTuples = [[string, string], [number, number]]; const add: PolymorphicFn<ValidTuples> = (...tuple) => { const [a, b] = tuple; if (typeof a === 'string' && typeof b === 'string') { return `${a}${b}`; } else if (typeof a === 'number' && typeof b === 'number') { return a + b; } throw new Error('Invalid arguments'); }; const num = add(1, 2); // number const str = add('1', '2'); // string
关键说明
- 方案一保留了你原有的泛型函数结构,通过精确的元组类型守卫缩小参数范围,再用类型断言填补TypeScript的推断缺口。
- 方案二利用映射类型将每个合法元组对应到具体的函数签名,让TypeScript能直接识别每个分支的返回类型,无需额外断言。
两种方案都能实现你需要的多态效果,且符合泛型计算的需求,避免了函数重载的写法。
内容的提问来源于stack exchange,提问作者Thomas Aribart
相关产品推荐
相关产品推荐

