TypeScript 工具类型详解与实战(二十)

在 TypeScript 开发中,工具类型(Utility Types)是提升代码质量和开发效率的强大武器。本文将详细介绍 TypeScript 中常用的工具类型,并通过实际示例帮助你更好地理解和应用这些工具类型。

什么是工具类型?

工具类型是 TypeScript 内置的一系列高级类型,它们通过泛型、映射类型和条件类型实现,帮助开发者快速创建和转换类型。使用工具类型可以显著提高代码的可复用性和类型安全性。

为什么需要工具类型?

在实际开发中,我们经常需要基于现有的类型创建新的类型。手动创建这些类型不仅繁琐,而且容易出错。工具类型提供了一种声明式的方式来转换和创建类型,大大提高了开发效率。

常用工具类型详解

1. Partial<T> - 所有属性可选

Partial<T> 将类型 T 的所有属性设置为可选。这在创建部分更新对象或处理表单数据时非常有用。

实例

假设我们有一个用户接口,包含必填属性:

interface User {
  id: number;
  name: string;
  email: string;
}

// 使用 Partial 将所有属性变为可选
type PartialUser = Partial
<User>;

const partialUser: PartialUser = { name: "Alice" };

console.log("部分用户:", JSON.stringify(partialUser));

运行结果:

部分用户: {"name":"Alice"}

2. Required<T> - 所有属性必填

Required<T> 与 Partial<T> 相反,将所有可选属性设置为必填。当需要确保对象包含所有属性时使用。

实例

假设我们有一个配置接口,属性都是可选的:

interface Config {
  host?: string;
  port?: number;
}

// 使用 Required 将所有可选属性变为必填
type RequiredConfig = Required
<Config>;

const config: RequiredConfig = {
  host: "localhost",
  port: 8080
};

console.log("配置:", JSON.stringify(config));

运行结果:

配置: {"host":"localhost","port":8080}

3. Readonly<T> - 所有属性只读

Readonly<T> 将所有属性设置为只读。创建后不允许修改的对象时非常有用。

实例

假设我们有一个用户接口:

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

// 使用 Readonly 将所有属性变为只读
type ReadonlyUser = Readonly
<User>;

const user: ReadonlyUser = {
  name: "Alice",
  age: 25
};

// 尝试修改只读属性会报错
// user.name = "Bob"; // 错误:只读属性不能修改

console.log("只读用户:", JSON.stringify(user));

4. Pick<T, K> - 选择属性

Pick<T, K> 从类型 T 中选择指定的属性 K 组成新类型。当你只需要某个类型的部分属性时使用。

实例

假设我们有一个完整的用户接口:

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
}

// 使用 Pick 选择指定的属性
type UserBasicInfo = Pick<User, "id" | "name">;

const user: UserBasicInfo = {
  id: 1,
  name: "Alice"
};

console.log("用户基本信息:", JSON.stringify(user));

运行结果:

用户基本信息: {"id":1,"name":"Alice"}

5. Omit<T, K> - 排除属性

Omit<T, K> 从类型 T 中排除指定的属性 K,返回剩余属性组成的新类型。与 Pick 相反,用于删除不需要的属性。

实例

假设我们有一个完整的用户接口:

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
}

// 使用 Omit 排除指定的属性
type UserWithoutPassword = Omit<User, "password">;

const user: UserWithoutPassword = {
  id: 1,
  name: "Alice",
  email: "a@b.com"
};

console.log("无密码用户:", JSON.stringify(user));

运行结果:

无密码用户: {"id":1,"name":"Alice","email":"a@b.com"}

6. Record<K, T> - 构造对象类型

Record<K, T> 构造一个对象类型,键的类型为 K,值的类型为 T。常用于创建键值对映射、字典类型等。

实例

假设我们有一个角色类型:

type Role = "admin" | "user" | "guest";

// 使用 Record 构造对象类型
type RolePermissions = Record<Role, string[]>;

const permissions: RolePermissions = {
  admin: ["read", "write", "delete"],
  user: ["read", "write"],
  guest: ["read"]
};

console.log("管理员权限:", permissions.admin);
console.log("访客权限:", permissions.guest);

运行结果:

管理员权限: read,write,delete

访客权限: read

7. Exclude<T, U> - 排除类型

Exclude<T, U> 从类型 T 中排除可以赋值给类型 U 的类型。主要用于联合类型,排除特定的类型成员。

实例

假设我们有一个联合类型:

type T = "a" | "b" | "c" | "d";

// 使用 Exclude 从 T 中排除指定类型
type NonABC = Exclude<T, "a" | "b" | "c">;

const value: NonABC = "d";

console.log("值:", value);

运行结果:

值: d

8. Extract<T, U> - 提取类型

Extract<T, U> 与 Exclude 相反,从类型 T 中提取可以赋值给类型 U 的类型。用于筛选联合类型中的特定成员。

实例

假设我们有一个混合联合类型:

type T = "a" | "b" | "c" | 1 | 2 | 3;

// 使用 Extract 从 T 中提取指定类型
type Letters = Extract<T, string>;

const letter: Letters = "a";

console.log("字母:", letter);

运行结果:

字母: a

9. NonNullable<T> - 排除空值

NonNullable<T> 从类型 T 中排除 null 和 undefined。确保类型不包含空值时使用。

实例

假设我们有一个混合类型:

type T = string | null | undefined | number;

// 使用 NonNullable 排除 null 和 undefined
type NotNull = NonNullable
<T>;

const value: NotNull = "hello";
value = 42;

// 尝试赋值 null 会报错
// value = null; // 错误:不能赋值 null

console.log("值:", value);

运行结果:

值: 42

10. ReturnType<T> - 获取返回类型

ReturnType<T> 获取函数类型 T 的返回类型。常用于从已存在的函数中提取返回类型。

实例

假设我们有两个函数:

function getUser() {
  return {
    name: "Alice",
    age: 25
  };
}

function getConfig() {
  return {
    host: "localhost",
    port: 8080
  };
}

// 使用 ReturnType 获取函数的返回类型
type UserType = ReturnType<typeof getUser>;
type ConfigType = ReturnType<typeof getConfig>;

const user: UserType = {
  name: "Bob",
  age: 30
};

const config: ConfigType = {
  host: "example.com",
  port: 3000
};

console.log("用户:", JSON.stringify(user));
console.log("配置:", JSON.stringify(config));

运行结果:

用户: {"name":"Bob","age":30}
配置: {"host":"example.com","port":3000}

注意事项

  • 工具类型都是泛型: 使用时应传入具体的类型参数。
  • 只读和可选: 工具类型可以组合使用,如 Partial<Readonly<T>>。
  • 内置类型: 这些工具类型都是 TypeScript 内置的,无需安装。
  • 自定义工具类型: 可以基于映射类型和条件类型创建自定义工具类型。

总结

TypeScript 工具类型是类型系统的重要组成部分,能够显著提升代码的类型安全性和可维护性。本文介绍了常用的工具类型及其应用场景,希望对你在 TypeScript 开发中有所帮助。善用工具类型,可以使你的代码更加简洁、高效和安全。

  • Partial<T>: 将所有属性变为可选
  • Required<T>: 将所有属性变为必填
  • Readonly<T>: 将所有属性变为只读
  • Pick<T, K>: 选择指定属性
  • Omit<T, K>: 排除指定属性
  • Record<K, T>: 构造对象类型
  • Exclude/Extract: 类型过滤
  • NonNullable: 排除 null 和 undefined
  • ReturnType: 获取函数返回类型

通过合理使用这些工具类型,你可以编写出更加健壮和高效的 TypeScript 代码。