前言
首先,TS内置的Partial类型工具非常好用,能够让索引类型比如一个interface的所有属性秒变可选属性。想象一下,后端小哥扔过来一个接口的文档,入参多到崩溃。文档中所有参数都被标记为了必填项,然后你作为一个认真友好的前端er,写出了一个超长长长类型,差点被自己不抛弃、不放弃、坚决不用any的精神所感动。正在回味之际,后端小哥突然说,参数都改为可选吧。。。一个一个的加?确实很崩溃,但Partial会让你充满自信!用法如下:
interface Params{
a:string
b:number
c:string
d:string
e:number[]
...
}
const params:Partial<Params> = {
a:'xxx'
}
这样再多的属性也不担心了,但是它也有一个缺陷,无法指定某些属性变为可选,而是一股脑的将全部属性都变为可选。比如,就想让上边Params接口的b与c属性变为可选,其他属性都还是保持必选的原样,内置的Partial就无法支持了,考虑到这是一个相当常见的场景,有必自己封装一个。自己就开始在前端群请教,希望快速白嫖一个方案,毕竟工期吃紧,不想在类型处理上花太多时间。
emmm,半天收了一个问号,也许是我问题的描述有误解,是需要完成“批量修改”的工作,而不是手动去一个个的加?号。不过此时我已决定,自己动手,丰衣足食。
实现
先看下原版Partial的实现,在此基础上,增加第二个表示【需要置为可选的属性集合】的类型参数。
先参考下TS内置Partial的实现:
type Partial<T> = { [P in keyof T]?: T[P]; }
自己实现了一个可选属性版本的:
type PartialPro<T, K extends keyof T> =
{ [P in K]?: T[P] } & Pick<T, Exclude<keyof T, K>>
整体的思路就是,拆分成两个索引类型【可选属性】、【剩余属性】最后用 & 交叉操作符求并集即可。
- 增加
K参数,并用extends约束它只能为T的属性(keyof T)的一个字符串联合类型。 [P in K]?: T[P]将包含在 K 中的属性都变为可选。- 用
Exclude<keyof T, K>筛选出不包含在 K 中的属性。用Pick筛选出来,保持原样即可。 - 最后,求两边的
&交叉即可。
写到这里,突然意识到步骤3也可以用TS内置的Omit来完成,原理一致,但会使代码更简洁一些,最终版本如下:
type PartialPro<T, K extends keyof T> =
{ [P in K]?: T[P] } & Omit<T,K>
总结
笔者之前对前端圈流行的“TS类型体操”运动一直不太感冒,对此的观点是:类型是帮助开发者与编程语言之间建立更友好沟通的工具,不应搞的太复杂,成为一种障碍,违背了初心。但它确实在某些场景下非常有用,作为一名熟练的TS开发者,至少要对内置的类型工具Exclude、Pick...及in、keyof、extends、typeof等这些操作符做到熟悉。比如这次对Partial类型进行扩展, 也算是从自己的实际需求出发,对这些TS基础操作有了更深入的理解。