大家好,我苏先生,一名热爱钻研、乐于分享的前端切图仔
github与好文
前言
上一节,我们通过12个内置工具类型初步感受了下类型体操是个啥:通过对一个已知类型编程生成一个新的类型
按照本小册的规划,还差87个...
本节,我们增加下难度,一起来实现下Merge、Diff、Flip
提示
对于语法层面的知识点本系列(类型体操开头的标题)不会展开说明哈,可以自行搜索学习其他大佬的优质文章或者等我后续更新补充
Merge
功能
合并两个已知类型,对于同名的key,向前进行覆盖,最后返回一个新类型
实现
它接收两个泛型参数:T、U
type Merge<T,U>
我们要判断的是key是否重复,因此我们必须拿到每一个key值,这在TypeScript中可以通过keyof来获取,并且它返回的是一个联合类型
type KOfT = keyof T
type KOfU = keyof U
然后,我们需要将KOfT和KOfU进行合并,生成一个具有不重复key的联合类型
type UniKey = KOfT | KOfU
我们使用|生成一个新的联合类型,在这一过程中,TypeScript会自动将重名的key进行剔除,如图所示
接着,我们需要拿到T中的每一个key到U中判断是否存在,因此我们还需要使用in关键字进行遍历
type UniKey = K in KOfT | KOfU
然后,我们使用extends关键字来构建条件类型,并分情况讨论:
1-K是KOfU的子类型,则直接使用KOfU的key作为合并后的结果
2-如果不是,则判断是否是KOfT的子类型,不是则使用never进行过滤
K extends KOfU ? S[K] : K extends KOfT ? T[K] : never
最后我们只需要借助映射类型进行下组装就可以啦:
type Merge<T, U> = {
[K in keyof T | keyof U]: K extends keyof U
? U[K]
: K extends keyof T
? T[K]
: never;
};
Diff
功能
找出两个对象类型的差异,并将差异部分组成一个新的类型返回
实现
它接收两个泛型参数
type Diff<T,U>
这两个泛形应该是对象类型,因此我们需要对其进行约束
type Diff<T extends object,U extends object>
同样的,我们要先将获取到两个类型的所有的key,然后才能考虑进行比较判断,关于key的获取,按照前文的实现,我们可以使用keyof先将其转换为联合类型再通过|来得到,但是这里我们换一种实现方式,我们先对接口进行合并然后再keyof取值
type UniKey = keyof (T&U)
接着我们拿到新类型UniKey中的每一个key,即K
K in UniKey
此时,如果我们能拿到两个类型共有的部分,那就可以使用在手写12个TypeScript内置工具类型中实现的Exclude工具类型进行排除:
1-获取类型的公共部分
type Com = keyof (T|U)
2-使用Exclude工具类型将key排除
type Excluded = Exclude<K,Com>
接着我们将其断言为新的类型Excluded
K in UniKey as Excluded
至于key对应的原泛型中的类型,我们使用索引访问类型直接从接口合并结果中取就好了
(T&U)[K]
最后,老规矩,我们通过映射类型进行下组装:
type Diff<T extends object,U extends object> = {
[K in keyof (T & U) as Exclude<K,keyof (T|U)>]:(T&U)[K]
}
推广时间:
我本来一直在推我的约定式路由库,不过目前看来收效甚微。所以我这里就不再花大篇幅去写了,就简单提一嘴
Flip
功能
交换一个对象类型的key和value,并返回一个新的类型
实现
它接受一个泛型类型T
type Flip<T>
由于对象类型的key只能是基本类型,但是其value却可以是任意类型,那就意味着,当我们使用索引类型T[K]来取value作为新的key时,将会有报错风险,因此,我们对key进行下约束
type Flip<T extends Record<string|number|boolean,any>>
接着我们获取到每一个key
K in keyof T
然后对其进行断言
type NewKey = K as T[K]
最后使用映射进行组装,并将K作为value即可
type Flip<T extends Record<string|number|boolean,any>> = {
[K in keyof T K as T[K]] : K
}
关于“理想很丰满,现实很骨感”这句话,许是对的!!!上文的写法会被TypeScript推断出错误
[图片上传失败...(image-c94871-1686489361019)]
按照错误提示,我们将boolean修改为symbol后的最终代码实现如下
type Flip<T extends Record<string|number|symbol,any>> = {
[K in keyof T as T[K]] : K
}
如果本文对您有用,希望能得到您的点赞和收藏