TypeScript在类型检查方面非常强大,但有时,当某些类型是其他类型的子集且你又需要为它们定义类型检查时,TypeScript就会变得冗余。

举个例子,假设有两种响应类型:

UserProfileResponse

1
2
3
4
5
6
7
interface UserProfileResponse {
id: number
name: string
email: string
phone: string
avatar: string
}

LoginResponse

1
2
3
4
interface LoginResponse {
id: number
name: string
}

我们可以为UserProfileResponse定义类型并为LoginResponse选择属性,而不是定义相同上下文LoginResponseUserProfileResponse的类型。

1
type LoginResponse = Pick<UserProfileResponse, "id" | "name">

下面我们将介绍一些可以帮助编写更佳代码的实用程序函数。

Uppercase

构建一个类型,定义其属性全是小写。然后使用Uppercase方法将其全部转化为大写。

1
2
3
4
5
6
7
type Role = "admin" | "user" | "guest";

// 不好的做法 💩
type UppercaseRole = "ADMIN" | "USER" | "GUEST";

// 正确的做法 ✅
type UppercaseRole = Uppercase<Role>; // "ADMIN" | "USER" | "GUEST"

Lowercase

与前面一个例子相反,先构建一个属性全是大写的类型,然后使用Lowercase方法将其全部转化为小写。

1
2
3
4
5
6
7
type Role = "ADMIN" | "USER" | "GUEST";

// 不好的做法 💩
type LowercaseRole = "admin" | "user" | "guest";

// 正确的做法 ✅
type LowercaseRole = Lowercase<Role>; // "admin" | "user" | "guest"

Capitalize

将所有属性的首字母大写。

1
2
3
4
5
6
7
type Role = "admin" | "user" | "guest";

// 不好的做法 💩
type CapitalizeRole = "Admin" | "User" | "Guest";

// 正确的做法 ✅
type CapitalizeRole = Capitalize<Role>; // "Admin" | "User" | "Guest"

Uncapitalize

Capitalize相反,将所有属性取消首字母大写。

1
2
3
4
5
6
7
type Role = "Admin" | "User" | "Guest";

// 不好的做法 💩
type UncapitalizeRole = "admin" | "user" | "guest";

// 正确的做法 ✅
type UncapitalizeRole = Uncapitalize<Role>; // "admin" | "user" | "guest"

Partial

将定义的接口中的属性设置为可选属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface User {
name: string;
age: number;
password: string;
}

// 不好的做法 💩
interface PartialUser {
name?: string;
age?: number;
password?: string;
}

// 正确的做法 ✅
type PartialUser = Partial<User>;

Required

与Partial相反,Required可以将接口中的可选属性设置为必需。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface User {
name?: string;
age?: number;
password?: string;
}

// 不好的做法 💩
interface RequiredUser {
name: string;
age: number;
password: string;
}

// 正确的做法 ✅
type RequiredUser = Required<User>;

Readonly

将属性设置为只读。

1
2
3
4
5
6
7
8
9
10
11
12
13

interface User {
role: string;
}

// 不好的做法 💩
const user: User = { role: "ADMIN" };
user.role = "USER";

// 正确的做法 ✅
type ReadonlyUser = Readonly<User>;
const user: ReadonlyUser = { role: "ADMIN" };
user.role = "USER"; // 将属性'role'设置为只读后,再次对'role'设值就会报错

Record

构造一个对象类型,其属性key是Keys,属性value是Tpye。被用于映射一个类型的属性到另一个类型。

1
2
3
4
5
6
7
8
9
10
11
12
interface Address {
street: string;
pin: number;
}

interface Addresses {
home: Address;
office: Address;
}

// Alternative ✅
type AddressesRecord = Record<"home" | "office", Address>;

Pick

就是从一个复合类型中,取出几个想要的类型的组合,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface User {
name: string;
age: number;
password: string;
}

// 不好的做法 💩
interface UserPartial {
name: string;
age: number;
}

// 正确的做法 ✅
type UserPartial = Pick<User, "name" | "age">;

Omit

Omit是TypeScript3.5新增的一个辅助类型,它的作用主要是:以一个类型为基础支持剔除某些属性,然后返回一个新类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

interface User {
name: string;
age: number;
password: string;
}

// 不好的做法 💩
interface UserPartial {
name: string;
age: number;
}

// 正确的做法 ✅
type UserPartial = Omit<User, "password">;

Exclude

将类型中其中一些属性排除,并创建排除属性后的新类型。

1
2
3
4
5
6
7
8

type Role = "ADMIN" | "USER" | "GUEST";

// 不好的做法 💩
type NonAdminRole = "USER" | "GUEST";

// 正确的做法 ✅
type NonAdmin = Exclude<Role, "ADMIN">; // "USER" | "GUEST"

Extract

它通过从可分配给联合的类型中提取所有联合成员来创建新类型。

1
2
3
4
5
6
7
8

type Role = "ADMIN" | "USER" | "GUEST";

// 不好的做法 💩
type AdminRole = "ADMIN";

// 正确的做法 ✅
type Admin = Extract<Role, "ADMIN">; // "ADMIN"

NonNullable

通过从类型中排除nullundefined来创建新类型。

1
2
3
4
5
6
7
8

type Role = "ADMIN" | "USER" | null;

// 不好的做法 💩
type NonNullableRole = "ADMIN" | "USER";

// 正确的做法 ✅
type NonNullableRole = NonNullable<Role>; // "ADMIN" | "USER"

感谢大家的阅读,祝大家编码快乐!