18.实用工具类型 (Utility Types) 副本.md 来源:https://my.feishu.cn/file/JZYpbnK1JoE4JGxmAZrcO3QFnsb 采集状态:已完成正文采集;已从查看代码模式逐行提取,行号 1 至 136 连续,并核对滚动到底。 随着我们对 TypeScript 的掌握日渐深入,我们会发现自己经常在进行一些重复性的“类型体操”:从一个现有类型中创建新类型,让所有属性变为可选,或者移除某些敏感属性。如果我们每次都手动编写这些转换逻辑,不仅会使代码变得冗长,还会增加出错的风险。 幸运的是,TypeScript 团队预见到了这些常见的需求,并为我们内置了一套功能强大的“类型工具箱”——**实用工具类型 (Utility Types)**。 这些工具就像是类型世界的“高阶函数”,它们接收一个或多个类型作为输入,然后返回一个经过转换的新类型。它们是 TypeScript 核心特性的集大成者,巧妙地运用了泛型、条件类型和映射类型,让复杂的类型转换变得轻而易举。掌握它们,是提升开发效率和代码质量的捷径。 本文将带你深入探索四个最核心、最常用的实用工具类型:`Partial`, `Readonly`, `Pick`, 和 `Omit`。 ### 1. `Partial`:让一切变为可选 * **作用**:构造一个新类型,并将 `T` 类型的所有属性设置为可选 (`?`)。 * **应用场景**:当你需要处理一个对象的“部分更新”时,`Partial` 无比实用。例如,在一个更新用户信息的函数中,用户可能只提交了需要修改的字段,而不是完整的用户对象。 **示例:** ```typescript interface User { id: number; username: string; email: string; bio: string; } // 这个函数用于更新用户信息 function updateUser(id: number, fieldsToUpdate: Partial) { // ... 在这里执行更新逻辑 // fieldsToUpdate 可以是 { username: 'newname' } // 也可以是 { email: 'new@email.com', bio: 'new bio' } } updateUser(1, { username: "RaycastAI" }); // 合法 updateUser(1, { email: "ai@raycast.com", bio: "Loves TS." }); // 合法 ``` `Partial` 自动生成了一个新类型 `{ id?: number; username?: string; ... }`,它允许我们传入 `User` 属性的任意子集,而无需手动为每个属性添加 `?`。 ### 2. `Readonly`:构建不可变壁垒 * **作用**:构造一个新类型,并将 `T` 类型的所有属性设置为只读 (`readonly`)。 * **应用场景**:当你希望创建一个不可变的数据对象时,`Readonly` 是你的首选。这在处理应用配置、或者从 API 获取并存储到状态管理中的数据时非常有用,可以防止意外的修改。 **示例:** ```typescript interface AppConfig { apiUrl: string; theme: 'dark' | 'light'; } const config: Readonly = { apiUrl: "https://api.raycast.com", theme: "dark", }; // 任何修改尝试都会在编译阶段被捕获 // config.theme = "light"; // 编译时错误: Cannot assign to 'theme' because it is a read-only property. ``` `Readonly` 确保了 `config` 对象在初始化后不会被篡改,为代码的稳定性增加了一道坚固的防线。 ### 3. `Pick`:精准挑选所需 * **作用**:从类型 `T` 中“挑选”出一组属性 `K`(`K` 是 `T` 中属性名的联合类型),并创建一个只包含这些属性的新类型。 * **应用场景**:当你需要从一个复杂的对象类型中,创建一个只包含部分关键信息的新类型时,`Pick` 非常有用。例如,从完整的用户对象中创建一个用于列表展示的“用户预览”类型。 **示例:** ```typescript interface User { id: number; username: string; email: string; createdAt: Date; lastLoginAt: Date; } // 创建一个只包含 id 和 username 的用户预览类型 type UserPreview = Pick; // UserPreview 等价于: // { // id: number; // username: string; // } const userForList: UserPreview = { id: 1, username: "RaycastAI", }; ``` `Pick` 让我们能够以声明式的方式,精确地从现有类型中派生出更小、更专注的子类型,极大地增强了代码的可读性和复用性。 ### 4. `Omit`:优雅地排除多余 * **作用**:与 `Pick` 相反,`Omit` 从类型 `T` 中构造一个新类型,该类型拥有 `T` 的所有属性,但**排除**了指定的属性集 `K`。 * **应用场景**:当你需要从一个类型中移除敏感信息或不需要的字段时,`Omit` 是最佳选择。例如,在将数据库中的用户对象返回给前端时,移除 `password` 字段。 **示例:** ```typescript interface UserWithPassword { id: number; username: string; email: string; passwordHash: string; } // 创建一个不包含 passwordHash 的公共用户类型 type PublicUser = Omit; // PublicUser 等价于: // { // id: number; // username:string; // email: string; // } function sendUserToClient(user: UserWithPassword): PublicUser { const { passwordHash, ...publicData } = user; return publicData; } ``` `Omit` 提供了一种非常清晰的方式来表达“我需要除了这些之外的所有属性”,这在处理数据转换和 API 边界时尤其有用。 ### 总结 实用工具类型是 TypeScript 赋予我们的高效“快捷方式”。它们封装了常见的类型转换逻辑,让我们能够以更少的代码,做更多的事情,同时保持类型系统的严谨性。 * **`Partial`**:用于部分更新,让所有属性可选。 * **`Readonly`**:用于创建不可变数据,让所有属性只读。 * **`Pick`**:用于创建子集类型,只保留指定属性。 * **`Omit`**:用于创建“净化”后的类型,排除指定属性。 这四个只是冰山一角。TypeScript 还提供了 `Required`, `Record`, `Exclude`, `ReturnType` 等一系列强大的工具。当你发现自己正在手动进行复杂的类型转换时,不妨先查阅一下 TypeScript 的官方文档——很可能已经有一个现成的实用工具类型在等着你了。