添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

[TypeScript] 如何合并联合类型中多个对象的属性

Jan 28, 2023

最近遇到一个场景,根据一个较复杂的数组类型来生成一个新的对象类型,我利用 条件类型 把数组类型中的数据提取出来后产生了 联合类型 ,接着需要从多个对象联合类型再转换成单个对象类型,于是就有了这篇文章,简单记录一下如何把联合类型中的多个对象合并成一个对象。

假如现在我们有一个联合类型是这样的:

  type UnionObject = { x: 1, y: 2 } | { x: 'a', z: 'c' }

一开始我写的工具类型是下面这样的:

  type OfUnion<T> = T extends Record<infer U, any> ? {
      [P in U]: T[P]
  } : never
  type UU = OfUnion<UnionObject>

我这里有点想当然了,因为采用了 extends 条件类型,返回的结果也会是一个联合类型,实际上做了无用功,得到的还是原来的类型。

既然是转换成一个对象类型,首先看下如何定义一个对象类型:

   // 知道 prop name
   type obj1 = { a: number }
   // prop name 未知
   type obj2 = { [prop: PropertyKey]: number }
   // 从泛型参数中提取 key
   type obj3<T> = {
    [key in keyof T]: T[key],    

这里不能直接把联合类型作为泛型参数传给 obj3 ,因为 keyof 是作用在对象上的,恰好我们处理的是一个对象的联合类型,那么就可以使用条件类型来对其进行分解,实现一个提取所有 key 的类型方法:

  type UnionKeys<T> = T extends unknown ? keyof T : never;
  type KK = UnionKeys<UnionObject> // "y" | "x" | "z"

UnionKeys<T> 方法返回的是我们的 union object 的 key 的联合类型。到这里完成了第一步,提取到了所有的 key ,把上面的 obj3<T> 做一下修改,跟 UnionKeys<T> 结合起来:

  type OfUnion<T> = {
    [P in UnionKeys<T>]: unknown

接下来就是计算每个属性所对应的值的类型。现在已经有了泛型 T 以及代表每个 key 值的 P 参数,通过这两个我们就可以拿到每个 P 所对应的值的类型,我们定义一个方法 UnionValues<T, K>从联合类型 T 中提取指定的 P 的值 ,这里需要用到条件类型中的 infer

   type UnionValues<T, K extends PropertyKey> = T extends Record<K, infer U> ? U : never;
   type V1 = UnionValues<T, 'z'> // 'c'

PropertyKey 是标准库提供的,它的类型是 string | number | symbol ,通用的对象属性的类型。我们已经拿到了具体的 P 的值,同样这里使用条件类型对 T 进行分解,结合 infer 就可以由编译器帮我们推断在 T 的每个 objectP 所对应的值的类型。把上面的代码整合起来:

* @author: Meowu * @source: fullstackbb.com type UnionKeys<T> = T extends unknown ? keyof T : never type UnionValues<T, K extends PropertyKey> = T extends Record<K, infer U> ? U: never; type OfUnion<T> = { [P in UnionKeys<T>]: UnionValues<T, P> type UnionObject = { x: 1, y: 2 } | { x: 'a', z: 'c' } type V1 = OfUnion<UnionObject> // { x: 1 | "a"; y: 2; z: "c"; }

这里我们就成功地把 Union 类型中的全部 object 合并成一个 object 。代码可以直接点击这个 链接 在官方 Playground 上查看。

在整个转换的过程中主要涉及到以下技术点的使用: