跳到主要内容

TypeScript 映射类型

TypeScript 的映射类型(Mapped Types)是一种强大的工具,它允许我们基于现有类型动态生成新的类型。通过映射类型,我们可以对现有类型的属性进行转换、筛选或修改,从而创建出符合需求的新类型。映射类型特别适用于处理对象类型,能够显著提升代码的可维护性和灵活性。

什么是映射类型?

映射类型的核心思想是遍历现有类型的属性,并对每个属性进行某种操作。TypeScript 提供了几种内置的映射类型,例如 PartialReadonlyPick,但你也可以自定义映射类型来满足特定需求。

基本语法

映射类型的基本语法如下:

typescript
type MappedType<T> = {
[P in keyof T]: NewType;
};
  • T 是原始类型。
  • PT 的每个属性键。
  • NewType 是对属性值进行转换后的新类型。

内置映射类型示例

TypeScript 提供了几种常用的内置映射类型:

  1. Partial<T>:将 T 的所有属性变为可选。
  2. Readonly<T>:将 T 的所有属性变为只读。
  3. Pick<T, K>:从 T 中选择指定的属性 K
  4. Record<K, T>:创建一个新类型,其属性键为 K,属性值为 T

示例:Partial<T>

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

type PartialUser = Partial<User>;

// 等价于:
// type PartialUser = {
// name?: string;
// age?: number;
// };

示例:Readonly<T>

typescript
type ReadonlyUser = Readonly<User>;

// 等价于:
// type ReadonlyUser = {
// readonly name: string;
// readonly age: number;
// };

示例:Pick<T, K>

typescript
type UserName = Pick<User, 'name'>;

// 等价于:
// type UserName = {
// name: string;
// };

示例:Record<K, T>

typescript
type UserMap = Record<string, User>;

// 等价于:
// type UserMap = {
// [key: string]: User;
// };

自定义映射类型

除了内置的映射类型,我们还可以根据需要自定义映射类型。例如,假设我们希望将对象的所有属性值转换为 string 类型:

typescript
type Stringify<T> = {
[P in keyof T]: string;
};

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

type StringifiedPerson = Stringify<Person>;

// 等价于:
// type StringifiedPerson = {
// name: string;
// age: string;
// };

条件映射

我们还可以在映射类型中使用条件类型,根据属性的原始类型进行不同的转换。例如,将 number 类型的属性转换为 string,其他类型保持不变:

typescript
type ConditionalStringify<T> = {
[P in keyof T]: T[P] extends number ? string : T[P];
};

type ConditionalPerson = ConditionalStringify<Person>;

// 等价于:
// type ConditionalPerson = {
// name: string;
// age: string;
// };

实际应用场景

映射类型在实际开发中有广泛的应用场景。以下是一些常见的例子:

1. 表单字段类型

在处理表单时,我们通常需要将表单字段的类型从必填转换为可选,以便在提交前进行部分验证:

typescript
interface FormFields {
username: string;
password: string;
email: string;
}

type OptionalFormFields = Partial<FormFields>;

// 等价于:
// type OptionalFormFields = {
// username?: string;
// password?: string;
// email?: string;
// };

2. API 响应类型

在开发 API 时,我们可能需要将某些字段标记为只读,以防止客户端修改:

typescript
interface ApiResponse {
id: number;
data: any;
}

type ReadonlyApiResponse = Readonly<ApiResponse>;

// 等价于:
// type ReadonlyApiResponse = {
// readonly id: number;
// readonly data: any;
// };

3. 动态生成类型

在某些情况下,我们需要根据一组键动态生成类型。例如,创建一个配置对象:

typescript
type ConfigKeys = 'theme' | 'language' | 'notifications';

type Config = Record<ConfigKeys, boolean>;

// 等价于:
// type Config = {
// theme: boolean;
// language: boolean;
// notifications: boolean;
// };

总结

映射类型是 TypeScript 中非常强大的功能,它允许我们基于现有类型动态生成新的类型。通过内置的映射类型(如 PartialReadonlyPick),我们可以轻松地修改对象类型的属性。此外,我们还可以自定义映射类型,以满足特定的需求。

掌握映射类型不仅能够提升代码的可维护性,还能让我们的类型系统更加灵活和强大。

附加资源与练习

  • 官方文档TypeScript 映射类型
  • 练习:尝试创建一个映射类型,将对象的所有属性值转换为 boolean 类型。
  • 挑战:结合条件类型和映射类型,实现一个将 nullundefined 属性过滤掉的映射类型。
提示

映射类型是 TypeScript 高级类型中的重要概念,建议多加练习以熟练掌握。通过实际项目中的应用,你将更好地理解其价值和灵活性。