TypeScript中如何为对象键不相交联合函数定义返回类型?
如何为属性不相交联合函数定义TypeScript返回类型
要让disjointUnion函数的返回类型能被精确推导,我们可以借助TypeScript的映射类型、条件类型,针对不同类型的键构造对应的结果类型。核心思路是区分三类键:仅存在于第一个对象的键、仅存在于第二个对象的键、两个对象共有的键,分别为它们生成匹配的类型结构。
步骤1:定义辅助类型
先提取三类键的类型,并构造对应部分的对象结构:
// 提取两个对象的公共键 type CommonKeys<T, U> = keyof T & keyof U; // 提取仅在第一个对象中存在的键 type OnlyTKeys<T, U> = Exclude<keyof T, keyof U>; // 提取仅在第二个对象中存在的键 type OnlyUKeys<T, U> = Exclude<keyof U, keyof T>; // 构造返回类型:合并三类键对应的结构 type DisjointUnionResult<T extends Record<string, any>, U extends Record<string, any>> = // 仅T有的键,保留原键和类型 { [K in OnlyTKeys<T, U>]: T[K] } & // 仅U有的键,保留原键和类型 { [K in OnlyUKeys<T, U>]: U[K] } & // 公共键:生成带0/1后缀的键,对应原对象的类型 { [K in CommonKeys<T, U> as `${string & K}0`]: T[K] } & { [K in CommonKeys<T, U> as `${string & K}1`]: U[K] }; // 可选:将交叉类型合并为单一对象类型(优化类型显示) type Merge<T> = { [K in keyof T]: T[K] };
步骤2:修改函数的返回类型
将函数的返回类型指定为Merge<DisjointUnionResult<T, U>>,同时调整内部的类型断言以适配类型推导:
export function disjointUnion< T extends Record<string, any>, U extends Record<string, any> >(obj1: T, obj2: U): Merge<DisjointUnionResult<T, U>> { const seenKeys = new Set<string>(); // 内部用最终返回类型做断言 const result = {} as Merge<DisjointUnionResult<T, U>>; for (const k of Object.keys(obj1)) { result[k as keyof typeof result] = obj1[k]; seenKeys.add(k); } for (const k of Object.keys(obj2)) { if (!seenKeys.has(k)) { result[k as keyof typeof result] = obj2[k]; continue; } // 处理重复键,添加索引后缀 const key0 = `${k}0` as keyof typeof result; const key1 = `${k}1` as keyof typeof result; result[key0] = result[k as keyof typeof result]; result[key1] = obj2[k]; delete result[k as keyof typeof result]; } return result; }
测试类型推导
现在测试你的示例代码,obj3的类型会被正确推导为{ job0: string; job1: string; age: number }:
const obj3 = disjointUnion({ job: "developer", age: 30 }, { job: "teacher" }); // 鼠标悬停obj3可查看类型:{ job0: string; job1: string; age: number }
说明
- 辅助类型通过
Exclude和keyof区分不同类型的键,用as关键字重映射公共键为带后缀的形式。 Merge类型的作用是把交叉类型(A & B & C)合并成单一对象类型,让类型显示更直观,不影响类型检查逻辑。- 函数内部的
as keyof typeof result是因为TypeScript无法在循环中自动推断键的合法性,需要手动断言,但外部的返回类型是完全精确的。
内容的提问来源于stack exchange,提问作者Adam B.
相关产品推荐
相关产品推荐

