查看原文
其他

TypeScript 类型体操:合并映射类型的处理结果为联合类型

IT服务圈儿 2023-02-06

The following article is from 神光的编程秘籍 Author 神说要有光

来源丨经授权转自  神光的编程秘籍
作者丨神说要有光

索引类型是 TypeScript 中的常见类型,它是聚合多个元素的类型,对象、类、元组等都是索引类型。

比如这样:

type Person = {
    name: string;
    age: number;
    hobbies: string[]
}

对索引类型做变换会用到映射类型的语法,它可以对索引类型的索引和值做一些变换,然后产生新的索引类型。

比如给每个索引加上 readonly 的修饰:

type ToReadonly<Obj> = {
    readonly [Key in keyof Obj]: Obj[Key]
}

返回的就是修改后的索引类型:

TypeScript 也内置了很多基于映射类型实现的工具类型,比如 Partial、Required 等。

总之,会了映射类型就能够对索引类型做各种变换了。

但是,这些都是对索引类型整体做的变换,变换的结果依然是一个索引类型。

有的时候是想把它们分开的。比如这种需求:

希望能把每个索引给分开。

这种怎么处理呢?

我是这样写的:

type SplitObj<Obj> = {
    [Key in keyof Obj]: {
        [Key2 in Key]: Obj[Key2]
    }
}[keyof Obj];

先看结果:

确实把每个索引给分开了。

再来讲为什么:

keyof Obj 我们知道是 key 构成的联合类型 'name' | 'age' | 'height'。

外层映射类型 [Key in keyof Obj] 就是对每个 Key 做处理,它值也是一个映射类型,而 Key2 来自于刚才的 Key,那么这样映射完之后的类型就是这样的:

这时你取 name 的值就是这样的:

而传入联合类型的时候,会分别传入每个类型做处理,也就是这样的:

所以直接在这里取 keyof Obj 的所有索引值:

总结一下:当我们需要把索引分开的时候,可以加一层映射类型,在值的位置对每个索引做处理,然后再传入 keyof Xxx 来取处理过后的值的联合类型。

这种套路还是很有用的,比如下面这个更复杂一点的案例:

给你一个索引类型,让你拿到所有索引的路径。

怎么做呢?

这里明显要对每个索引都做路径的处理,然后把所有的路径合并。

根据刚才学过的写法,可以这样写:

type DFS<Obj> = {
  [Key in keyof Obj]: Key
}[keyof Obj];

这样就能把每个索引分开处理:

然后具体的处理是需要递归的,如果值是索引类型就继续递归,否则就结束。递归过程中记录下路径。

type DFS<Obj> = {
  [Key in keyof Obj]: 
    Key extends string
      ? Obj[Key] extends Record<stringany>
        ? Key | `${Key}.${DFS<Obj[Key]>}`
        : Key
      : never
}[keyof Obj];

这里因为 Key 默认推导出来的不是 string,所以加一层 Key extends string 的判断之后,再用 Key的时候就是 string 了。

判断 Obj[Key] 是不是索引类型,也就是是不是 Record<string, any>,如果是就递归调用 DFS,并且记录当前路径到结果里,如果不是就返回当前 Key。

这样的结果就是对每个索引做了递归的处理,并且把所有索引的处理结果合并到了一起:

回顾下这个案例,它也是要把每个索引的处理结果分开,通过联合类型合并在一起。具体每个索引是做递归的处理,记录路径。

总结

索引类型是 TypeScript 中的常见类型,可以通过映射类型的语法来对它做一些修改,生成新的索引类型。

但如果你想对每个索引分别做处理,并且把结果合并为一个联合类型的时候,可以加一层映射类型来分别处理每个索引,再取 keyof Xxx,也就是每个索引类型的处理结果构成的联合类型。

这种套路在需要把索引分开处理,再把结果合并的场景下是很有用的。

1、再有人问你如何实现订单到期关闭,就把这篇文章发给他!

2、当Redis碰上@Transactional,有大坑,要注意!

3、React内部是如何实现cache方法的?

4、擦,就加了一个字段还能出事故?

5、Go 大佬良心发现,愿意给 map 加清除了?

点分享

点点赞

点在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存