如何将TypeScript对象类型方法合并为重载函数签名?
TypeScript 对象类型转重载函数类型解决方案
问题背景
现有如下TypeScript类型:
type Def = { m1(a1: A1, b1: B1): R1 m2(a2: A2): R2 m3(a3: A3, b3: B3, c3: C3): R3 }
需要将其转换为重载函数类型:
type F = ((name: 'm1', a1: A1, b1: B1) => R1) & ((name: 'm2', a2: A2) => R2) & ((name: 'm3', a3: A3, b3: B3, c3: C3) => R3)
该类型用于实现动态调度器方法。目前已通过映射类型、Parameters和ReturnType工具类型生成了中间类型H:
type H = { m1: (name: 'm1', a1: A1, b1: B1) => R1, m2: (name: 'm2', a2: A2) => R2, m3: (name: 'm3', a3: A3, b3: B3, c3: C3) => R3, }
但无法将H合并为单一的重载函数签名,需解决该问题。
解决方案
可以通过索引访问类型结合UnionToIntersection工具类型,将H的属性值类型联合转换为交叉类型,进而得到目标重载函数类型。
1. 实现UnionToIntersection工具类型
利用TypeScript条件类型的分发特性,实现联合类型转交叉类型的工具:
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
2. 从H推导目标重载函数类型
通过H[keyof H]提取H中所有函数类型的联合,再用UnionToIntersection转换为交叉类型(TypeScript会将函数交叉类型解析为重载):
type F = UnionToIntersection<H[keyof H]>;
完整可运行示例
// 示例基础类型 type A1 = number; type B1 = string; type R1 = boolean; type A2 = symbol; type R2 = bigint; type A3 = Date; type B3 = RegExp; type C3 = ArrayBuffer; type R3 = Map<any, any>; // 原始对象类型 type Def = { m1(a1: A1, b1: B1): R1 m2(a2: A2): R2 m3(a3: A3, b3: B3, c3: C3): R3 }; // 生成中间类型H type H = { [K in keyof Def]: (name: K, ...args: Parameters<Def[K]>) => ReturnType<Def[K]> }; // 联合转交叉工具类型 type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; // 推导目标重载函数类型 type F = UnionToIntersection<H[keyof H]>; // 验证类型正确性 declare const f: F; f('m1', 123, 'abc'); // 符合重载,返回boolean f('m2', Symbol('test')); // 符合重载,返回bigint f('m3', new Date(), /regex/, new ArrayBuffer(8)); // 符合重载,返回Map<any, any>
原理说明
H[keyof H]会提取H中所有属性的类型,得到函数类型联合:((name: 'm1', ...) => R1) | ((name: 'm2', ...) => R2) | ((name: 'm3', ...) => R3)UnionToIntersection利用函数参数的逆变特性,将联合类型转换为交叉类型。TypeScript对函数交叉类型的解析规则就是重载函数,因此最终得到符合预期的重载签名。
内容的提问来源于stack exchange,提问作者pqnet
相关产品推荐
相关产品推荐

