泛型接口扩展通用解法:TypeScript接口类型合并需求
泛型实现TypeScript接口合并与merge函数
刚好碰到过类似的需求,咱们一步步来解决这个问题:你有两个接口A和B,想要实现一种泛型方案,让合并后的类型效果等同于interface D——也就是后面的类型(B)会覆盖前面类型(A)的同名属性,同时把两个类型的所有非同名属性都整合进来,还要写一个merge函数来实现对象的合并并返回对应的合并类型。
第一步:定义合并类型Merge<T, U>
TypeScript原生的交叉类型T & U虽然能合并属性,但如果有同名属性会变成never,不符合咱们的需求。所以咱们需要先把前面类型T中与U重复的属性去掉,再和U交叉,这样U的属性就能完全覆盖T的同名属性了。
// 核心合并类型:U的属性会覆盖T的同名属性,同时保留双方独有属性 type Merge<T, U> = Omit<T, keyof U> & U;
简单解释下:
Omit<T, keyof U>:从T中移除所有在U里存在的属性键,只保留T独有的属性& U:把处理后的T和完整的U做交叉,这样U的属性会完全保留,同时整合T的独有属性
第二步:实现merge函数
有了合并类型,咱们就可以写对应的合并函数了,用对象扩展运算符就能实现浅合并(刚好匹配你示例里的需求,sub对象会直接被覆盖):
function merge<T, U>(t: T, u: U): Merge<T, U> { // 用扩展运算符实现对象浅合并,u的属性会覆盖t的同名属性 return { ...t, ...u } as Merge<T, U>; }
这里用as Merge<T, U>做类型断言,是因为TypeScript有时候没法自动推导扩展运算符后的精确类型,手动断言能让类型更准确。
验证效果
咱们把你的示例代码放进来测试,看看是不是完全符合预期:
// 原接口A interface A { name: string; value: number; sub: { query: number[] }; old: number; } // 原接口B interface B { name: string; value: string; sub: { query: string[] }; new: boolean; } // 期望的目标接口D interface D { name: string; value: string; sub: { query: string[] }; old: number; new: boolean; } // 验证Merge<A, B>是否等于D type C = Merge<A, B>; // 此时C的类型完全等同于D,可以通过类型检查确认: const test: C = { name: "test", value: "string", // 类型为string,说明B的属性覆盖了A的number类型 sub: { query: ["1", "2"] }, // query为string[],正确覆盖 old: 123, new: true }; // 测试merge函数 const a: A = { name: "a", value: 123, sub: { query: [1,2] }, old: 456 }; const b: B = { name: "b", value: "hello", sub: { query: ["x","y"] }, new: false }; const merged = merge(a, b); // merged的类型是Merge<A,B>,也就是D的类型,所有属性都符合预期 console.log(merged.value); // 类型为string,值为"hello" console.log(merged.sub.query); // 类型为string[],值为["x","y"]
这样就完全实现了你想要的效果:合并后的类型保留了B的同名属性,同时整合了A的独有属性old和B的独有属性new。
内容的提问来源于stack exchange,提问作者Viatorus
相关产品推荐
相关产品推荐

