携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第16天,点击查看活动详情
前言
现在有这么一个对象类型,描述了人类的部分信息:
interface Human {
name: string
age: number
occupation: {
name: string
workingHours: number
},
say: () => void
}
现在因为需求的原因,需要根据这个 Human 对象类型,新建出一个具有相同属性的对象,而且可以指定部分属性可选,指定的可选属性根据需求会有变动。
代码
总所周知,TS 中有个 ? 属性修饰符,表示可选,因为新建的对象类型的属性必须和 Human 相同,所以可以考虑使用映射类型,将 Human 的属性映射到新的对象类型上面,同时将属性处理为可选:
// 映射类型
type ObjLimit1<T> = {
[key in keyof T]?: T[key]
}
// 根据 Human 创建出一个具有相同的可选属性的对象
type NewHuman = ObjLimit1<Human>
但是这种只会让全部属性都可选,可以再考虑传入一个泛型 K,用来指定可选属性的名称。
那这个 K 毫无疑问,必须的是 T 属性名称的联合类型,接着对从 K 中映射出的属性都加上可选操作符即可:
// 注意,必须定义 K 为 T 属性名称合集作为约束
// 因为后续需要使用 T[key] 取出对应属性的值的类型
type ObjLimit1<T, K extends keyof T> = {
[key in K]?: T[key]
}
// 指定 name 和 age 两个属性可选,使用l
type NewHuman = ObjLimit1<Human, 'name' | 'age'>
可以看到,只有 name 和 age 是可选的,但也只有这两个属性了,其他属性并没有加入新建的对象类型中去。
因为其他属性没有被包含在 K 里面,而且其他属性不是被指定为可选的,也不适合在这里处理。
其实接下来只要得到没有 name 和 age 的对象类型,把这个对象类型和上面实现对象类型进行交叉操作 & 即可。
那么目标很简单,就是获取没有 name 和 age 的对象类型,可以借助 Exclude 剔除指定的属性即可:
type ModifierLimit2<T, K extends keyof T> = {
// 利用 Exclude 剔除指定的属性
[P in Exclude<keyof T, K>]: T[P]
}
// 指定剔除 name 和 age
type NewHuman = ObjLimit2<Human, 'name' | 'age'>
可以看到,已经获取了 name 和 age 之外的属性了,接下来就是进行交叉操作即可, 完整代码:
type ObjLimit1<T, K extends keyof T> = {
[key in K]?: T[key]
}
type ObjLimit2<T, K extends keyof T> = {
[key in Exclude<keyof T, K>]: T[key]
}
// 两种类型 交叉
type ObjLimit<T, K extends keyof T> = ObjLimit1<T, K> & ObjLimit2<T, K>
type NewHuman = ObjLimit<Human, 'name' | 'age'>
可以看到,对象的属性还是那些属性,但是 name 和 age 已经被处理为可选。
至此,就是指定属性为可选的基本思路。