「TypeScript」你必须要知道的 TS 高级技能点 —— Utility Types

2,355 阅读5分钟

本文正在参加「金石计划」

flag:每月至少产出三篇高质量文章~

Typescript 中的实用类型是一些预定义的泛型类型,可用于操作或创建其他新类型。这些类型在所有 Typescript 项目中都是全局可用的,因此无需添加任何依赖项即可使用它们。

一、官方内置 utility types

1、Partial<Type>

构建一个类型,将类型的所有属性设置为可选。这个工具将返回一个表示给定类型的所有子集的类型。下面来看一个例子:

ray-so-export (2).png

2、Required<Type>

构建一个由Type的所有属性组成的类型,设置为必填。与 Partial 相反。

ray-so-export (3).png

3、Record<Keys, Type>

构建一个对象类型,其属性键是 Keys,其属性值是 Type。这个工具可以用来将一个类型的属性映射到另一个类型。

ray-so-export (4).png

4、Omit<Type, Keys>

通过从 Type 中选取所有属性,然后删除 Keys(字符串字面或字符串字面的联合)来构造一个类型。

ray-so-export (2).png

我们还可以通过传递一个union类型:

ray-so-export (3).png

5、Pick<Type, Keys>

通过从 Type 中选取属性集合 Keys(字符串字头或字符串字头的联合)来构造一个类型。

ray-so-export (4).png

5.1 Partial + Pick

ray-so-export (5).png

5.2 Omit + Partial + Pick

ray-so-export (6).png

6、Readonly<Type>

构建一个类型,Type的所有属性设置为只读,这意味着构建的类型的属性不能被重新分配。

ray-so-export (7).png

这个 utility types 对于表示将在运行时失败的赋值表达式很有用(即当试图重新分配一个冻结对象的属性时)。比如,Object.freeze:

function freeze<Type>(obj: Type): Readonly<Type>;

7、Mutable<Type>

创建一个 Mutable 类型帮助程序,允许你将所有只读类型转换为可变类型。

ray-so-export (8).png

8、Exclude<UnionType, ExcludedMembers>

通过从 UnionType 中排除可分配给 ExcludedMembers 的所有 union 成员来构造一个类型。

ray-so-export (9).png

你还可以exlude多个联合成员:

ray-so-export (10).png

9、Extract<Type, Union>

通过从 Type 中提取可分配给 Union 的所有 union成员,构造一个类型。

ray-so-export (5).png

10、ReturnType<Type>

构建一个由函数 Type 的返回类型组成的类型。

ray-so-export (8).png

11、Parameters<Type>

从一个函数类型 Type 的参数中使用的类型构建一个元组类型。

ray-so-export (6).png

11.1 ThisParameterType<Type>

提取一个函数类型的 this参数 的类型,如果该函数类型没有 this参数,则为 unknown

ray-so-export (10).png

11.2 OmitThisParameter<Type>

移除 Typethis参数。如果 Type 没有明确声明的 this参数,结果只是 Type。否则,一个没有 this参数 的新函数类型将从 Type 创建。泛型被擦除,只有最后的重载签名被传播到新的函数类型。

ray-so-export (12).png

11.3 ConstructorParameters<Type>

从构造函数的类型中构造一个元组或数组类型。它产生一个具有所有参数类型的元组类型(如果 Type 不是一个函数,则是一个 never 类型)。

ray-so-export (7).png

12、ThisType<Type>

这个工具类型并不返回一个转换后的类型。相反,它作为一个上下文的 this类型 的标记。注意,必须启用 noImplicitThis 才能使用这个工具类型。

ray-so-export (13).png

在上面的例子中,makeObject 的参数中的方法对象有一个包括 ThisType<D & M> 的上下文类型,因此方法对象中 this 的类型是 { x: number, y: number } & { moveBy(dx: number, dy: number): number }。注意,方法属性的类型是如何同时作为推理目标和方法中this类型的来源的。

ThisType<T> 标记接口只是在 lib.d.ts 中声明的一个空接口。除了在对象字面的上下文类型中被识别之外,该接口的行为与任何空接口一样。

13、NonNullable<Type>

通过从 Type 中排除 nullundefined 来构造一个类型。

ray-so-export (14).png

14、InstanceType<Type>

构建一个由 Type 中构造函数的实例类型组成的类型。

ray-so-export (9).png

15、Awaited<Type>

这种类型是为了模拟像 async 函数中的 awaitPromises 上的 .then() 方法这样的操作,特别是它们递归地解除 Promises 的方式。

ray-so-export (18).png

Awaited + ReturnType

ray-so-export (16).png

二、社区知名 utility types 库

1、utility-types

比较知名,也比较老(目前只支持到 v3.7)的一个实用类型的集合,补充了 TypeScript 内置的映射类型和别名(想想静态类型的 "lodash"),可以 copy 一些自己想要的到项目中使用。

地址:utility-types

image.png

2、type-fest

项目地址:type-fest

image.png

有大量的类型可供选择:

image.png

3、ts-toolbelt

项目地址:ts-toolbelt

ts-toolbelt 是目前最大的,也是经过最多测试的类型库,具有+200个实用工具。我们的类型集合打包了市场上一些最先进的映射类型、条件类型和递归类型。

image.png

三、建议:不要过度使用 utility types

有时候,过度使用实用程序类型可能会导致代码难以理解和维护。特别是将多种类型嵌套在一起。比如下面这个例子:

type User = {
  id: number;
  name: string;
  email: string;
};

type VeryComplexUser = Readonly<Pick<User, "id">> &
  Partial<Omit<User, "email">> &
  { email: User["email"] | null } &
  { role: string } &
  Record<"email", string> & {
    role: User["name"] extends "admin" ? "admin" : "user";
  } & {
    name: User["name"];
  } & {
    id: { [K in keyof User["id"]]: User["id"][K] };
  } & {
    email: User["email"] & string;
  } & {
    role: Exclude<User["role"], "guest">;
  };

头大不头大~

end~

欢迎关注之前的几篇文章: