映射类型与条件类型

TypeScript 的高级类型系统提供了强大的工具来创建灵活且类型安全的代码,其中映射类型(Mapped Types)和条件类型(Conditional Types)是两个极为重要的概念。本文将深入探讨这两种类型的用法和应用场景。

映射类型:转换现有类型的利器

映射类型允许我们基于现有类型创建新类型,通过遍历现有类型的属性并进行转换。

基本语法

typescript 复制代码
type MappedType<T> = {
    [P in keyof T]: NewType;
};

常见内置映射类型

TypeScript 提供了一些内置的映射类型:

  1. Partial:使所有属性变为可选

    typescript 复制代码
    type Partial<T> = {
        [P in keyof T]?: T[P];
    };
  2. Readonly:使所有属性变为只读

    typescript 复制代码
    type Readonly<T> = {
        readonly [P in keyof T]: T[P];
    };
  3. Pick:从类型中选择部分属性

    typescript 复制代码
    type Pick<T, K extends keyof T> = {
        [P in K]: T[P];
    };
  4. Record:创建具有指定键类型和值类型的类型

    typescript 复制代码
    type Record<K extends keyof any, T> = {
        [P in K]: T;
    };

自定义映射类型示例

typescript 复制代码
// 将所有属性变为可为null
type Nullable<T> = {
    [P in keyof T]: T[P] | null;
};

// 为所有属性添加getter方法
type Getters<T> = {
    [P in keyof T as `get${Capitalize<string & P>}`]: () => T[P];
};

条件类型:类型层面的条件判断

条件类型允许我们根据条件表达式选择不同的类型,类似于值层面的三元运算符。

基本语法

typescript 复制代码
T extends U ? X : Y

常见应用

  1. 类型过滤

    typescript 复制代码
    type Filter<T, U> = T extends U ? T : never;
  2. 提取函数返回类型

    typescript 复制代码
    type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
  3. 提取函数参数类型

    typescript 复制代码
    type Parameters<T> = T extends (...args: infer P) => any ? P : never;

分布式条件类型

当条件类型作用于联合类型时,它会自动分布到每个联合成员上:

typescript 复制代码
type Distributed = string | number extends string ? true : false;  // false
type Distributed2 = (string extends string ? true : false) | 
                   (number extends string ? true : false);  // true | false → boolean

映射类型与条件类型的结合

这两种高级类型结合使用时能产生更强大的效果:

typescript 复制代码
// 将类型中所有函数属性的返回类型改为Promise
type Promisify<T> = {
    [P in keyof T]: T[P] extends (...args: infer A) => infer R 
        ? (...args: A) => Promise<R> 
        : T[P];
};

// 移除类型中所有可选标记
type Concrete<T> = {
    [P in keyof T]-?: T[P];
};

实际应用案例

  1. API响应类型处理
typescript 复制代码
type ApiResponse<T> = {
    data: T;
    error: string | null;
    status: number;
};

// 创建只包含数据的类型
type ResponseData<T> = ApiResponse<T>['data'];

// 创建错误处理类型
type ErrorResponse = Pick<ApiResponse<any>, 'error' | 'status'>;
  1. 表单验证
typescript 复制代码
type Validator<T> = {
    [P in keyof T]: (value: T[P]) => boolean;
};

type User = {
    name: string;
    age: number;
};

const userValidator: Validator<User> = {
    name: (value) => value.length > 0,
    age: (value) => value >= 18
};

总结

映射类型和条件类型是TypeScript类型系统中极为强大的工具,它们允许开发者:

  • 基于现有类型创建新类型,减少重复代码
  • 实现复杂的类型转换和操作
  • 构建更加灵活和可维护的类型定义
  • 在编译时捕获更多潜在错误

掌握这些高级类型技术将显著提升你的TypeScript技能,使你能够构建更加健壮和类型安全的应用程序。