image.png

看了 TypeScript 官方文档发现对泛型的运用不太明确,所以整理了一些常用的工具泛型。什么是工具泛型?这些泛型接口定义大多数是语法糖(简写), 甚至你可以在 Typecript 包中的 lib.d.ts 中找到它的定义, 目前新版的 Typescript 已经包含了大部分, 没有提供的我会特别注明。

Partial

Partial 作用是将传入的属性变为可选项.
熟悉 JavaScript 的童鞋很好理解 keyof 和 in 两个关键字, keyof 可以用来取得一个对象接口的所有 key 值.
比如:

  1. interface Shopee {
  2. name: string;
  3. address: string
  4. }
  5. type T = keyof Shopee // -> "name" | "address"

in 则是遍历枚举类型
例如:

  1. type Keys = "Shopee" | "FE"
  2. type Obj = {
  3. [p in Keys]: any
  4. } // -> { Shopee: any, FE: any }

keyof 产生联合类型, in 则可以遍历枚举类型, 所以他们经常一起使用, 看下 Partial 源码

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

上面语句的意思是 keyof T 拿到 T 所有属性名, 然后 in 进行遍历, 将值赋给 P, 最后 T[P] 取得相应属性的值.
结合中间的 ? 我们就很好理解了 Partial 的含义了.

Required

Required 的作用是将传入的属性变为必选项, 源码如下

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

在这里我们看到一个神奇的用法 -?, 其实很好理解就是将可选项代表的 ? 减掉, 从而让这个类型变成必选项. 与之对应的还有个 +? , 这个含义自然与 -? 之前相反, 它是用来把属性变成可选项的.

Mutable (未包含)

不止是可以对必选和可选做 + 和 - ,类似地, 对只读属性也可以 + 和 -, 这里要说的不是变量的之间的进行加减而是对 readonly 进行加减.
以下代码的作用就是将 T 的所有属性的 readonly 移除,你也可以写一个相反的出来.

  1. type Mutable<T> = {
  2. -readonly [P in keyof T]: T[P]
  3. }

Readonly

将传入的属性变为只读选项, 源码如下

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


Record

将 K 中所有的属性的值转化为 T 类型

  1. type Record<K extends keyof any, T> = { [P in K]: T };

Pick

从 T 中取出 一系列 K 的属性

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

Exclude

在 TS 2.8 中引入了一个条件类型, 示例如下

  1. T extends U ? X : Y

以上语句的意思就是 如果 T 是 U 的子类型的话,那么就会返回 X,否则返回 Y 甚至可以组合多个

  1. type Shopee<T> =
  2. T extends string ? "string" :
  3. T extends number ? "number" :
  4. T extends boolean ? "boolean" :
  5. T extends undefined ? "undefined" :
  6. T extends Function ? "function" :
  7. "object";

对于联合类型来说会自动分发条件,例如 T extends U ? X : Y , T 可能是 A | B 的联合类型, 那实际情况就变成(A extends U ? X : Y) | (B extends U ? X : Y)
有了以上的了解我们再来理解下面的工具泛型, Exclude 的源码:

  1. type Exclude<T, U> = T extends U ? never : T;

栗子:

  1. type T = Exclude<1 | 2, 1 | 3> // -> 2

Extract

源码:

  1. type Extract<T, U> = T extends U ? T : never;

从源码可以看出 Extract 的作用是提取出 T 包含在 U 中的元素, 简单说就是从 T 中提取出 U

Omit (未包含)

用之前的 Pick 和 Exclude 进行组合, 实现忽略对象某些属性功能, 源码如下

  1. type Omit<T, K> = Pick<T, Exclude<keyof T, K>>

栗子:

  1. type Foo = Omit<{name: string, age: number}, 'name'> // -> { age: number }

ReturnType

在这个语法糖中我们先需要了解一下 infer 这个关键字, 在条件类型语句中, 我们可以用 infer 声明一个类型变量并且对它进行使用, 我们可以用它获取函数的返回类型, 源码如下:

  1. type ReturnType<T> = T extends (
  2. ...args: any[]
  3. ) => infer R
  4. ? R
  5. : any;

其实这里的 infer R 就是声明一个变量来承载传入函数签名的返回值类型, 简单说就是用它取到函数返回值的类型方便之后使用. 常用的情况就是单不知道函数调用会返回什么类型时,为了类型统一,可以直接用这个语法糖包裹函数获得返回值的类型,具体用法

  1. function Shopee(x: number): Array<number> {
  2. return [x];
  3. }
  4. type fn = ReturnType<typeof Shopee>;

AxiosReturnType (未包含)

开发经常使用 axios 进行封装 API层 请求, 通常是一个函数返回一个 AxiosPromise, 现在我想取到它的 Resp 类型, 根据上一个工具泛型的知识我们可以这样写.

  1. import { AxiosPromise } from 'axios' // 导入接口
  2. type AxiosReturnType<T> =
  3. T extends (...args: any[]) => AxiosPromise<infer R> ? R : any

栗子:

  1. type Resp = AxiosReturnType<Api> // 泛型参数中传入你的 Api 请求函数