Yuzhe's Blog

yuzhes

645 - Diff 差集

Diff 差集

题目链接

题目

获取两个对象类型 OO1 的差集,返回一个对象,包含仅存在于其中一个对象中的属性(对称差集)。

type Foo = { name: string; age: string }
type Bar = { name: string; age: string; gender: number }

type Result = Diff<Foo, Bar> // { gender: number }

解答

type Diff<O, O1> = {
  [K in Exclude<keyof O, keyof O1> | Exclude<keyof O1, keyof O>]: 
    K extends keyof O ? O[K] : K extends keyof O1 ? O1[K] : never
}

更简洁的写法,利用 Omit

type Diff<O, O1> = Omit<O & O1, keyof O & keyof O1>

深入解析

理解问题

我们需要求两个对象类型的对称差集——只出现在其中一个对象中的属性。这不是简单的 Exclude,因为需要双向考虑两边的 key。

方案一:显式排除 Key

type Diff<O, O1> = {
  [K in Exclude<keyof O, keyof O1> | Exclude<keyof O1, keyof O>]: 
    K extends keyof O ? O[K] : K extends keyof O1 ? O1[K] : never
}

拆解:

  1. Exclude<keyof O, keyof O1> — O 有但 O1 没有的 key
  2. Exclude<keyof O1, keyof O> — O1 有但 O 没有的 key
  3. 联合 — 所有独有的 key
  4. 映射值 — 用条件类型取正确的值类型

方案二:交集 + Omit(优雅)

type Diff<O, O1> = Omit<O & O1, keyof O & keyof O1>

更优雅:

  1. O & O1 — 交集包含两个对象的所有属性
  2. keyof O & keyof O1 — 两个对象共有的 key
  3. Omit<..., ...> — 移除共有的 key,剩下的就是差异

原理

Diff<Foo, Bar> 为例:

用方案二:

边界情况

// 相同对象 → 空对象
Diff<{ a: 1 }, { a: 1 }> // {}

// 完全不同 → 所有属性的并集
Diff<{ a: 1 }, { b: 2 }> // { a: 1; b: 2 }

// 有重叠但类型不同(交集语义)
Diff<{ a: string }, { a: number; b: boolean }> // { b: boolean }

要点总结

  1. 对称差集需要双向考虑两个对象的 key
  2. Omit<O & O1, keyof O & keyof O1> 是最简洁的解法
  3. 交集类型&)合并所有属性,配合 Omit 可以过滤我们需要的
  4. 这个模式适用于计算两个类型之间”有什么变化”