TypeScript 实用类型
最后修改于 2025 年 3 月 5 日
TypeScript 实用类型是内置工具,可简化常见的类型操作。它们提供了一种简洁的方式来转换、提取或修改现有类型,而无需从头开始编写自定义类型定义。本教程将通过实际示例探讨一系列实用类型,以展示它们的实用性和多功能性。
诸如 Partial、Pick 和 ReturnType 等实用类型利用了 TypeScript 的高级功能(如映射类型、条件类型和推断)来满足频繁的类型需求。它们在 TypeScript 中是全局可用的,无需导入,旨在提高在从对象操作到函数分析的各种场景下的生产力和类型安全性。
Partial
Partial<T> 实用类型使类型的所有属性都变为可选。
interface User {
name: string;
age: number;
}
const partialUser: Partial<User> = { name: "Alice" };
console.log(partialUser.name); // Output: Alice
Partial<User> 实用类型将 User 接口转换为一个类型,其中 name 和 age 是可选的({ name?: string; age?: number })。这使得 partialUser 可以省略 age 而不会出现类型错误,这与原始的 User 不同,后者需要两个属性。
输出“Alice”证实了 name 属性是可访问的,而 age 保持未定义但有效。此实用类型非常适合表单更新或 API 有效负载等场景,其中仅提供部分字段,从而简化了部分对象定义。
Pick
Pick<T, K> 实用类型通过从现有类型中选择特定属性来创建一个类型。
interface Product {
id: number;
name: string;
price: number;
}
const pickedProduct: Pick<Product, "id" | "name"> = { id: 1, name: "Laptop" };
console.log(pickedProduct.name); // Output: Laptop
Pick<Product, "id" | "name"> 从 Product 中仅使用 id 和 name 属性构建一个新类型,得到 { id: number; name: string }。pickedProduct 对象排除了 price,如果包含它(例如 price: 100)将导致类型错误。
输出“Laptop”显示了所选属性的使用情况。此实用类型对于创建类型的子集很有用,例如在仅将相关字段传递给函数或组件时,从而提高类型精度并减少样板代码。
Omit
Omit<T, K> 实用类型通过从现有类型中排除特定属性来创建一个类型。
interface Item {
id: number;
name: string;
category: string;
}
const omittedItem: Omit<Item, "category"> = { id: 1, name: "Book" };
console.log(omittedItem.name); // Output: Book
Omit<Item, "category"> 从 Item 生成一个不包含 category 属性的类型,得到 { id: number; name: string }。omittedItem 对象符合此要求,排除了 category,并且添加它(例如 category: "Books")将导致类型检查失败。输出“Book”证实了保留的 name 属性。此实用类型通过允许排除而不是包含来补充 Pick,使其在从对象类型中删除敏感或不相关字段时非常方便。
Readonly
Readonly<T> 实用类型使类型的所有属性都变为只读。
interface Config {
host: string;
port: number;
}
const config: Readonly<Config> = { host: "localhost", port: 8080 };
// config.host = "example.com"; // Error: Cannot assign to 'host' because it is read-only
console.log(config.host); // Output: localhost
Readonly<Config> 将 Config 转换为 { readonly host: string; readonly port: number },防止初始化后修改属性。config 对象可以定义但不能更改——尝试 config.host = "example.com" 会触发编译时错误,如注释所示。输出“localhost”反映了不可变值。此实用类型非常适合定义常量或保护数据结构免遭意外更改,从而提高诸如配置管理等场景下的代码可靠性。
Required
Required<T> 实用类型使类型的所有可选属性都变为必需。
interface OptionalUser {
name?: string;
age?: number;
}
const requiredUser: Required<OptionalUser> = { name: "Bob", age: 30 };
console.log(requiredUser.age); // Output: 30
Required<OptionalUser> 将 OptionalUser 的可选属性(name?: string、age?: number)转换为必需属性({ name: string; age: number })。requiredUser 对象必须包含 name 和 age——省略其中任何一个(例如 { name: "Bob" })都会导致类型错误。
输出 30 证实了 age 的存在。此实用类型对于强制执行完整性非常有用,例如确保在可选阶段之后,最终数据结构中提供了所有字段。
Record
Record<K, T> 实用类型创建一个具有类型 K 的键和类型 T 的值的类型。
type Status = "success" | "error" | "pending";
const statusMessages: Record<Status, string> = {
success: "Operation completed",
error: "Something went wrong",
pending: "In progress"
};
console.log(statusMessages.success); // Output: Operation completed
Record<Status, string> 构建一个类型,其中来自 Status 联合("success" | "error" | "pending")的每个键都映射到一个 string 值,得到 { success: string; error: string; pending: string }。statusMessages 对象必须定义所有键并具有字符串值,并且缺少键(例如省略 pending)将导致类型检查失败。
输出“Operation completed”显示了 success 值。此实用类型非常适合创建具有类型安全键和值的字典或查找表,从而简化诸如状态码到消息的映射。
ReturnType
ReturnType<T> 实用类型提取函数类型的返回类型。
function getName(): string {
return "Alice";
}
type NameType = ReturnType<typeof getName>;
const name: NameType = "Bob";
console.log(name); // Output: Bob
ReturnType<typeof getName> 使用 typeof 来捕获函数的类型,从而推断 getName 的返回类型,即 string。NameType 别名变为 string,允许 name 被赋值为任何字符串值,例如“Bob”。输出“Bob”证实了这一点。此实用类型对于从函数派生类型而不手动指定它们非常强大,在诸如高阶函数或回调中的函数结果类型化等场景中很有用。
Exclude
Exclude<T, U> 实用类型会从可以分配给另一个类型的联合中移除类型。
type Status = "success" | "error" | "pending" | "loading"; type NonErrorStatus = Exclude<Status, "error" | "loading">; const status: NonErrorStatus = "success"; console.log(status); // Output: success
Exclude<Status, "error" | "loading"> 过滤 Status 联合("success" | "error" | "pending" | "loading"),移除 "error" 和 "loading",留下 "success" | "pending"。status 变量可以是 "success" 或 "pending",但赋值 "error" 会失败。
输出“success”反映了一个有效值。此实用类型非常适合缩小联合类型,例如从更广泛的状态类型中排除错误状态,以用于特定的逻辑分支。
Extract
Extract<T, U> 实用类型会保留联合中可以分配给另一个类型的类型。
type Event = "click" | "hover" | "scroll" | string; type MouseEvent = Extract<Event, "click" | "hover">; const event: MouseEvent = "click"; console.log(event); // Output: click
Extract<Event, "click" | "hover"> 从 Event 联合("click" | "hover" | "scroll" | string)中仅保留 "click" 和 "hover",因为它们与指定的类型匹配,得到 "click" | "hover"。event 变量可以是其中任何一个,但 "scroll" 或随机字符串(如 "focus")会失败(尽管原始联合中包含 string)。
输出“click”证实了一个有效的提取值。此实用类型对于隔离联合的特定子集很有用,例如从更广泛的事件类型中提取与鼠标相关的事件。
NonNullable
NonNullable<T> 实用类型会从类型中移除 null 和 undefined。
type Value = string | null | undefined; const safeValue: NonNullable<Value> = "Hello"; console.log(safeValue); // Output: Hello
NonNullable<Value> 从 Value 联合(string | null | undefined)中剥离 null 和 undefined,留下 string。safeValue 变量必须是字符串——赋值 null 或 undefined 会导致类型检查失败。
输出“Hello”反映了这种非可空约束。此实用类型对于确保值已定义至关重要,例如在进行空值检查后或在使用已解析可空性的 API 时,从而在关键操作中增强类型安全性。
最佳实践
- 使用实用类型处理常见模式: 利用
Partial或Pick等实用类型进行频繁转换,以避免冗余的自定义类型。 - 组合使用实用类型: 链式使用实用类型(例如
Pick<Partial<T>, K>)进行复杂转换,以最大化其灵活性。 - 根据用例选择实用类型: 根据包含还是排除更符合您的意图,选择正确的实用类型(例如
Omit而不是Pick)。 - 记录用法: 注释说明应用实用类型的原因(例如,“使字段对部分更新可选”),以澄清对维护者的意图。
- 测试类型约束: 将对象或值与实用类型变量进行比较,以确保它们强制执行预期的结构或约束。
- 避免过度依赖: 谨慎使用实用类型,在某些特殊情况下,当它们的简单性超过实用类型的复杂性时,可以回退到显式类型。
来源
本教程通过实际示例介绍了 TypeScript 实用类型。使用这些内置工具来简化类型操作并增强代码安全性。