03.TypeScript 常见泛型工具详解

03.TypeScript 常见泛型工具详解

什么是泛型工具类型?

在 TypeScript 中,泛型工具类型(Generic Utility Types)是一些内置的泛型类型,它们可以帮助我们更方便地操作和转换类型。这些工具类型是 TypeScript 类型系统的重要组成部分,能够让我们以类型安全的方式进行复杂的类型操作。

1. Partial

Partial 将类型 T 的所有属性都变为可选。

1.1 实例

ts 复制代码
interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

// 使用 Partial 创建一个可选属性的用户类型
function updateUser(id: number, updates: Partial<User>): void {
  // 更新用户的部分属性
  console.log(`Updating user ${id} with`, updates);
}

// 可以只传递部分属性
updateUser(1, { name: "Alice" });
updateUser(2, { email: "bob@example.com", age: 30 });

1.2 实现

ts 复制代码
// Partial 的实现原理
type MyPartial<T> = {
  [P in keyof T]?: T[P];
};

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

2. Required

Required 将类型 T 的所有属性都变为必填。

2.1 实例

ts 复制代码
interface UserOptions {
  id?: number;
  name?: string;
  email?: string;
  isActive?: boolean;
}

// 使用 Required 确保所有属性都是必需的
function createUser(user: Required<UserOptions>): void {
  console.log("Creating user:", user);
}

// 必须提供所有属性
createUser({ 
  id: 1, 
  name: "Alice", 
  email: "alice@example.com", 
  isActive: true 
});

2.2 实现

ts 复制代码
// Required 的实现原理
type MyRequired<T> = {
  [P in keyof T]-?: T[P];
};

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

3. Readonly

Readonly 将类型 T 的所有属性都变为只读。

3.1 实例

ts 复制代码
interface Config {
  apiUrl: string;
  timeout: number;
  retries: number;
}

// 使用 Readonly 创建只读配置
const config: Readonly<Config> = {
  apiUrl: "https://api.example.com",
  timeout: 5000,
  retries: 3
};

// 以下操作会报错,因为属性是只读的
// config.apiUrl = "https://new-api.example.com"; // Error!

3.2 实现

ts 复制代码
// Readonly 的实现原理
type MyReadonly<T> = {
  readonly [P in keyof T]: T[P];
};

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

4. Record

Record<K, T> 创建一个对象类型,属性键为 K,属性值为 T。

4.1 实例

ts 复制代码
// 创建一个映射类型,键为字符串,值为数字
const scores: Record<string, number> = {
  "Alice": 95,
  "Bob": 87,
  "Charlie": 92
};

// 创建一个具有特定键的配置对象
type Role = "admin" | "user" | "guest";

interface Permissions {
  canRead: boolean;
  canWrite: boolean;
  canDelete: boolean;
}

const rolePermissions: Record<Role, Permissions> = {
  admin: { canRead: true, canWrite: true, canDelete: true },
  user: { canRead: true, canWrite: true, canDelete: false },
  guest: { canRead: true, canWrite: false, canDelete: false }
};

4.2 实现

ts 复制代码
// Record 的实现原理
type MyRecord<K extends keyof any, T> = {
  [P in K]: T;
};

// 源码
type Record<K extends keyof any, T> = {
    [P in K]: T;
};

5. Pick

Pick<T, K> 从类型 T 中选择一组属性 K。

5.1 实例

ts 复制代码
interface User {
  id: number;
  name: string;
  email: string;
  age: number;
  isActive: boolean;
}

// 只选择用户的基本信息
type UserInfo = Pick<User, "name" | "email">;

const userInfo: UserInfo = {
  name: "Alice",
  email: "alice@example.com"
};

// 选择用户的身份信息
type UserIdentity = Pick<User, "id" | "name">;

function greetUser(user: UserIdentity): string {
  return `Hello, ${user.name} (ID: ${user.id})`;
}

5.2 实现

ts 复制代码
// Pick 的实现原理
type MyPick<T, K extends keyof T> = {
  [P in K]: T[P];
};

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

6. Omit

Omit<T, K> 从类型 T 中排除一组属性 K。

6.1 实例

ts 复制代码
interface User {
  id: number;
  name: string;
  email: string;
  password: string;
  age: number;
  isActive: boolean;
}

// 排除敏感信息,创建安全的用户类型
type PublicUser = Omit<User, "password">;

function getUserProfile(userId: number): Promise<PublicUser> {
  // 从数据库获取用户信息,但不包含密码
  return fetch(`/api/users/${userId}`).then(res => res.json());
}

// 排除多个敏感属性
type SafeUser = Omit<User, "password" | "email">;

6.2 实现

ts 复制代码
// Omit 的实现原理
type MyOmit<T, K extends keyof T> = {
  [P in Exclude<keyof T, K>]: T[P];
}

// Omit 的实现原理
type MyOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

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

7. Exclude

Exclude<T, U> 从类型 T 中排除 U 类型的所有属性。

7.1 实例

ts 复制代码
// 从联合类型中排除特定类型
type Status = "pending" | "approved" | "rejected" | "draft";

// 排除草稿状态
type PublishedStatus = Exclude<Status, "draft">;

// 排除多个状态
type ActiveStatus = Exclude<Status, "draft" | "rejected">;

// 在数字联合类型中使用
type Numbers = 1 | 2 | 3 | 4 | 5;
type OddNumbers = Exclude<Numbers, 2 | 4>;

7.2 实现

ts 复制代码
// Exclude 的实现原理
type MyExclude<T, U> = T extends U ? never : T;

// 源码
type Exclude<T, U> = T extends U ? never : T;

8. Extract

Extract<T, U> 从类型 T 中提取 U 类型的所有属性。

8.1 实例

ts 复制代码
// 从联合类型中提取特定类型
type Status = "pending" | "approved" | "rejected" | "draft";

// 提取已完成的状态
type CompletedStatus = Extract<Status, "approved" | "rejected">;

// 提取字符串类型
type Mixed = string | number | boolean;
type StringsOnly = Extract<Mixed, string>;

// 提取数字类型
type NumbersOnly = Extract<Mixed, number>;

8.2 实现

ts 复制代码
// Extract 的实现原理
type MyExtract<T, U> = T extends U ? T : never;

// 源码
type Extract<T, U> = T extends U ? T : never;

9. NonNullable

NonNullable 从类型 T 中排除 null 和 undefined。

9.1 实例

ts 复制代码
// 创建可能包含 null 或 undefined 的联合类型
type NullableString = string | null | undefined;

// 排除 null 和 undefined
type StrictString = NonNullable<NullableString>;

// 在函数参数中使用
function processText(text: NullableString): number {
  // 使用类型断言或类型检查
  const strictText: StrictString = text as StrictString;
  return strictText.length; // 现在不会有 null 或 undefined 错误
}

// 在数组中使用
type NullableArray = (string | null)[];
type StrictArray = NonNullable<NullableArray>[number][];

9.2 实现

ts 复制代码
// NonNullable 的实现原理
type MyNonNullable<T> = T extends null | undefined ? never : T;
// 源码
type NonNullable<T> = T & {};

10. Parameters

Parameters 从函数类型 T 中提取参数类型。

10.1 实例

ts 复制代码
// 定义一个函数类型
function greet(name: string, age: number): string {
  return `Hello, ${name}! You are ${age} years old.`;
}

// 获取函数的参数类型
type GreetParams = Parameters<typeof greet>;

// 使用参数类型
const params: GreetParams = ["Alice", 25];

// 在高阶函数中使用
function callWithParams<T extends (...args: any[]) => any>(
  fn: T,
  ...args: Parameters<T>
): ReturnType<T> {
  return fn(...args);
}

// 调用函数
const result = callWithParams(greet, "Bob", 30);

10.2 实现

ts 复制代码
// Parameters 的实现原理
type MyParameters<T extends (...args: any) => any> = 
  T extends (...args: infer P) => any ? P : never;

// 源码
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;

11. ReturnType

ReturnType 从函数类型 T 中提取返回类型。

11.1 实例

ts 复制代码
// 定义函数
function getUser(id: number) {
  return {
    id,
    name: "Alice",
    email: "alice@example.com",
    isActive: true
  };
}

// 获取函数的返回值类型
type UserType = ReturnType<typeof getUser>;

// 使用返回值类型
const user: UserType = {
  id: 1,
  name: "Bob",
  email: "bob@example.com",
  isActive: false
};

// 在异步函数中使用
async function fetchUser(id: number) {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}

type FetchUserReturnType = ReturnType<typeof fetchUser>;
type UserDataType = Awaited<FetchUserReturnType>; // 获取 Promise 解析后的类型

11.2 实现

ts 复制代码
// ReturnType 的实现原理
type MyReturnType<T extends (...args: any) => any> = 
  T extends (...args: any) => infer R ? R : any;

// 源码
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

12. 总结

TypeScript 的泛型工具类型为我们提供了强大的类型操作能力:

  1. Partial 和 Required 允许我们控制属性的可选性;
  2. Readonly 帮助我们创建不可变的数据结构;
  3. Record<K, T> 用于创建键值对映射类型;
  4. Pick<T, K> 和 Omit<T, K> 允许我们选择或排除特定属性;
  5. Exclude<T, U> 和 Extract<T, U> 用于从联合类型中提取或排除类型;
  6. NonNullable 从类型中排除 null 和 undefined;
  7. Parameters 和 ReturnType 用于提取函数参数和返回值类型。
相关推荐
烛阴6 小时前
TypeScript 函数重载入门:让你的函数签名更精确
前端·javascript·typescript
随笔记7 小时前
react中函数式组件和类组件有什么区别?新建的react项目用函数式组件还是类组件?
前端·react.js·typescript
葡萄城技术团队7 小时前
TypeScript 进阶必备!5 个实用工具类型,帮你写出更健壮的前端代码
typescript
定栓7 小时前
Typescript入门-对象讲解
前端·javascript·typescript
ssshooter19 小时前
VSCode 自带的 TS 版本可能跟项目TS 版本不一样
前端·面试·typescript
Lsx_1 天前
TypeScript 是怎么去查找类型定义的?
前端·javascript·typescript
知识分享小能手2 天前
Vue3 学习教程,从入门到精通,Axios 在 Vue 3 中的使用指南(37)
前端·javascript·vue.js·学习·typescript·vue·vue3
任磊abc2 天前
vscode无法检测到typescript环境解决办法
ide·vscode·typescript
烛阴2 天前
精简之道:TypeScript 参数属性 (Parameter Properties) 详解
前端·javascript·typescript