如何递归排除TypeScript两个类型间的公共属性?
递归排除TypeScript类型的公共属性并保留可选性
要实现递归排除两个类型的公共属性,同时保留原类型的可选属性修饰符,可使用以下类型工具:
// 辅助类型:判断是否为普通对象(排除数组、函数、日期等特殊类型) type IsObject<T> = T extends object ? T extends Array<any> | Function | Date | RegExp ? false : true : false; // 核心递归类型:排除与K共有的属性,递归处理嵌套对象,保留可选性 type ExcludeCommonRecursively<T, K> = { // 通过Pick+Exclude筛选需保留的键,自动保留原属性的可选修饰符 [U in keyof Pick<T, Exclude<keyof T, keyof K>>]: // 若属性值为普通对象,递归处理;数组则递归处理元素类型,否则直接保留原类型 IsObject<T[U]> extends true ? ExcludeCommonRecursively<T[U], K> : T[U] extends Array<infer V> ? IsObject<V> extends true ? ExcludeCommonRecursively<V, K>[] : T[U] : T[U]; };
验证结果
用你提供的示例类型测试:
type One = { one: number; two?: number; three: number; sub: { two?: number; three: number; } } type Two = { three: number; } type Three = ExcludeCommonRecursively<One, Two>;
生成的Three类型与期望完全一致:
type Three = { one: number; two?: number; sub: { two?: number; } }
之前实现的问题分析
非递归版本的局限
非递归的ExcludeType仅处理顶层属性,无法深入嵌套对象内部排除公共属性,因此sub中的three未被移除。
原递归版本的问题
- 键排除逻辑错误:原代码中
Exclude<keyof T, keyof U>是笔误,应改为Exclude<keyof T, keyof K>,否则无法正确排除与K共有的属性。 - 丢失可选属性修饰符:直接使用
[U in ...]映射时,会丢失原属性的?修饰符;通过Pick<T, Exclude<keyof T, keyof K>>先筛选键再映射,可自动保留可选性。 - 对象判断逻辑错误:原代码中
T[U] extends U的判断逻辑完全错误,需通过IsObject明确区分普通对象与其他类型。
扩展说明
IsObject类型默认排除了数组、函数等特殊对象,若需处理这些类型,可调整其判断逻辑。- 实现支持数组元素的递归处理:若数组元素为对象,会自动排除其与
K共有的属性。
内容的提问来源于stack exchange,提问作者Yokozuna59
相关产品推荐
相关产品推荐

