通俗易懂TypeScript的高级类型方法------Partial, Required, Pick, Omit, Readonly, NonNullable

姜志行
2023-12-01

Partial(可选属性)

作用:Partial将类型的属性变成可选的

举例

假如现在有个interface

interface IActivityItem {
  id: number;
  title: string;
  links: string;
}

然后某一天我想把IActivityItem身上的属性变成可选的,那可能就会再写一个Interface

interface IActivityItem2 {
  id?: number;
  title?: string;
  links?: string;
}

但是如果属性过多的话,就得写很多额外的代码,这个时候就可以用Partial

type IActivityItem2 =Partial<IActivityItem>
//等同于
interface IActivityItem2 {
  id?: number;
  title?: string;
  links?: string;
}

原理

type Partial<T> = {
    [P in keyof T]?: T[P];
};

那么 Partial<T> 是如何实现类型转化的呢?

遍历入参 T ,获得每一对 key, value

将每一对中的 key 变为可选,即添加 ?

希望得到的是由 key, value 组成的新类型

以上对应到 TypeScript 中是如何实现的呢?

keyof

keyof,即 索引类型查询操作符,我们可以将 keyof 作用于泛型 T 上来获取泛型 T 上的所有 public 属性名构成的 联合类型

例如

type unionKey = keyof IActivityItem

// unionKey 结果如下,其获得了接口类型 IUser 中的所有属性名组成的联合类型
type unionKey = "id" | "title" | "links"

in

我们需要遍历 IActivityItem ,这时候 映射类型就可以用上了,其语法为 [P in Keys]

  • P:类型变量,依次绑定到每个属性上,对应每个属性名的类型
  • Keys:字符串字面量构成的联合类型,表示一组属性名(的类型),可以联想到上文 keyof 的作用

T[P]

我们可以通过 keyof 查询索引类型的属性名,那么如何获取属性名对应的属性值类型呢?

这里就用到了 索引访问操作符,与 JavaScript 种访问属性值的操作类似,访问类型的操作符也是通过 [] 来访问的,即 T[P],其中”中括号“中的 P 与 [P in keyof T] 中的 P 相对应。

例如

type unionKey = keyof IActivityItem  // "id" | "title" | "links"

type values = IActivityItem[unionKey] // number|string|string 属性值类型组成的联合类型

最后我们希望得到的是由多个 key, value 组成的新类型,故而在 [P in keyof T]?: T[P]; 外部包上一层大括号。

Required(必选属性)

作用:和Partial刚好相反,Required 将类型的属性变成必选

举例

假如有一个interface

interface IActivityItem {
  id: number;
  title?: string;
  links?: string;
}

某一天我想把里面的属性全变为必选的,我就会重新写一个interface

interface IActivityItem2 {
  id: number;
  title: string;
  links: string;
}

但如果我用Required就会方便很多

type IActivityItem2 = Required<IActivityItem>
  //等同于
interface IActivityItem2 {
  id: number;
  title: string;
  links: string;
}

原理

type Required<T> = { 
    [P in keyof T]-?: T[P] 
};

Pick(部分选择)

作用:从某个类型中挑出一些属性出来

举例

假如有一个interface

interface IActivityItem {
  id: number;
  title: string;
  links: string;
  start_time: number;
  end_time: number;
  live_time: number;
  index: number;
}

某一天,我想要一个新的interface,他只拥有id,titlle这两个属性,那我会重写一个interface

interface IActivityItem2 {
  id: number;
  title: string;
}

但如果使用Pick非常方便去实现

type IActivityItem2 = Pick<IActivityItem,'id','title'>
//等同于
interface IActivityItem2 {
  id: number;
  title: string;
}

原理

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

Omit(属性忽略)

作用:与Pick相反,Pick用于拣选出我们需要关心的属性,而Omit用于忽略掉我们不需要关心的属性

举例

假如有一个interface

interface IActivityItem {
  id: number;
  title: string;
  links: string;
  
}

现在我想要重新写一个interface,它只有id, title两个属性

interface IActivityItem2 {
  id: number;
  title: string;
  
}

那么这个时候Omit就派上用场了

type IActivityItem2 =Omit<IActivityItem,'links'>
//等同于
interface IActivityItem2 {
  id: number;
  title: string;
}

原理

type Omit<T, K extends keyof any> = 
      Pick<T, Exclude<keyof T, K>>;

Readonly(只读属性)

作用:Readonly 的作用是将某个类型所有属性变为只读属性,也就意味着这些属性不能被重新赋值。

举例

interface Person {
    readonly id: number;
    name: string;
    age?: number;
}
let tom: Person = {
    id: 89757,
    name: 'Tom',
    gender: 'male'
};
tom.id = 9527;      //  Cannot assign to 'id' because it is a constant or a read-only property

原理

type Readonly<T> = {
 readonly [P in keyof T]: T[P];
}; 

NonNullable(过滤null及undefined)

作用:是用来过滤类型中的null及undefined类型

type T0 = NonNullable<string | number | undefined>; // string | number

type T1 = NonNullable<string[] | null | undefined>;  // string[]

原理

type NonNullable<T> = T extends 
                      null | undefined 
                      ? never : T;

 类似资料: