TS工具类型-给对象指定的属性加上readonly

605 阅读4分钟

4月日新计划更文活动 第2天

前言

我们都知道 TS 的内置工具类型 Readonly 能够给一个对象的所有属性都加上 readonly 关键字,那么我们要是想要给一个对象的某一些字段加上这个关键字,应该要怎么操作,最近碰到了这个需求,今天用一篇文章记录一下。

在这之前,我们要来理解一个概念,就是 TS 中的 & 和 | 分别代表什么 ,如果想直接看工具类型的可以跳到文章最后

& 交集

在数学上面的交集,我们理解为两个集合公共拥有的部分,比方说我现在有两个集合 [1,2,3] 和 [2,3,4] 那么他们的交集应该是 [2,3] 这个交集可以理解为两个集合都拥有的部分。

那么在 TS 当中,假如是两个 基本类型 之间使用这个符号,比如说

type AAA = number & string

image.png

很明显,他并不可能存在,因为不存在一个属性,他又是 字符串,又是 数字。

但是用在对象类型上的时候就有着不一样的现象:

type OOO = {a:1} & {b:2}

image.png

根据上面的理解,我们可以认为是一个对象,它既要是 {a:1} 又要是 {b:2} 那么他能怎么办,是不是就只能同时包含两个人共有的属性。

| 并集

在数学上面的并集,我们理解为由所有属于A或属于B的元素组成的集合,比方说我现在有两个集合 [1,2,3] 和 [2,3,4] 那么他们的交集应该是 [1,,2,3,4] 这个并集可以理解为两个集合拥有的部分的合并。

那么在 TS 当中,假如是两个 基本类型 之间使用这个符号,比如说

type AAA = number & string

image.png

这就是我们常说的联合类型,代表这个属性既可以是 字符串,又可以是 数字

但是用在对象类型上的时候就有着不一样的现象:

type OOO = {a:1} | {b:2}

image.png

根据上面的理解,我们可以认为是一个对象,它既可以是 {a:1} 又可以是 {b:2} 那么他就会变成两个对象都含有的属性,很明显两个对象都含有的属性为空,这里我们拿一个其他例子可以更好地理解一下。

type OOO = {a:1, b:3} | {b:2}

image.png

启发:

一个全是 readonly 修饰语的对象类型 一旦 &并 上另一个对象类型,并且另一个对象类型中有同样的字段,但是并没有使用 readonly 修饰符,那么生产的新对象,这个 readonly 修饰符会消失,

image.png

那么可以根据这个特性,我们在为一个对象的指定 key 添加 readonly 修饰符

通过先将这个对象的所有词条都变成 readonly 然后并上另一个 除了需要的 key 以外的 key 都含有的对象,就能够实现

实现

根据上面的思路,我们可以列出几点需要的操作,

  1. 需要获取需求对象中,除了需要的key以外的key组成的新对象

  2. 需要将原有对象全部加上 readonly 修饰符

我们就假设一个新的泛型类型 AddReadonly 他的第一个参数为目标对象,第二个参数是一个字符串联合类型,字符串就是我们需要添加 readonly 的key

然后从第一步开始,我们能够发现,第一步有点像一个内置类型 Omit 的定义,从一个对象当中剔除某些属性,那么我们是不是就可以利用这个内置类型来剔除 里面的所有的 然后得到一个新的类型

type SSS = {
    a:1,
    b:2,
    c:3
}
  
type SSSS = Omit<SSS, 'a' | 's'>
// type SSSS = {
//     b: 2;
//     c: 3;
// }

第二步,我们还是可以通过 TS 的内置 Readonly 类型,来将 中的所有属性都加上 Readonly 修饰符。

type SSS = {
    a:1,
    b:2,
    c:3
}
  
type SSSS = Readonly<SSS>
// type SSSS = {
//    readonly a:1,
//    readonly b: 2;
//    readonly c: 3;
// }

然后剩下的就简单了,根据分析所说只需要取两个对象的交集,就是我们最后所需要的结果,结合一下两个内置类型,新建一个 AddReadonly 类型

type AddReadonly<T extends object, U extends string> = Readonly<T> & Omit<T,U>
  
type A1 = {
    b: number,
    c: number
}
  
type E1 = AddReadonly<A1, 'b' | 's'>
  
type E2 = {[P in keyof E1]: E1[P]}

image.png