Skip to content

条件类型

概述

条件类型(Conditional Types)是 TypeScript 2.8 引入的强大特性,它允许我们根据类型条件来选择不同的类型。条件类型使用三元运算符 T extends U ? X : Y 的语法,类似于 JavaScript 中的三元表达式,但作用于类型层面。通过条件类型,我们可以创建更加灵活和智能的类型系统,实现类型级别的条件判断和类型转换。

为什么需要条件类型

在没有条件类型的情况下,我们无法根据类型关系动态选择类型:

typescript
// 没有条件类型:无法根据类型关系选择不同的类型
type GetType<T> = T;  // 只能返回 T 本身

// 使用条件类型:可以根据类型关系选择不同的类型
type GetType<T> = T extends string ? string : number;

条件类型让我们能够:

  1. 根据类型关系选择类型:根据一个类型是否满足某个条件,选择不同的类型
  2. 实现类型转换:将一种类型转换为另一种类型
  3. 创建类型工具:构建复杂的类型工具和类型操作
  4. 类型级编程:在类型层面实现逻辑判断

基本语法

基本条件类型

条件类型使用 extends 关键字和三元运算符 ? :

typescript
// 基本语法:T extends U ? X : Y
// 如果 T 可以赋值给 U,则类型为 X,否则为 Y
type IsString<T> = T extends string ? true : false;

// 使用示例
type Test1 = IsString<string>;   // 类型:true
type Test2 = IsString<number>;  // 类型:false
type Test3 = IsString<"hello">; // 类型:true(字面量类型是 string 的子类型)

条件类型的工作原理

条件类型通过检查类型 T 是否可以赋值给类型 U 来判断条件:

typescript
// 检查 T 是否为数组类型
type IsArray<T> = T extends any[] ? true : false;

// 使用示例
type Test1 = IsArray<number[]>;      // 类型:true
type Test2 = IsArray<string[]>;      // 类型:true
type Test3 = IsArray<number>;        // 类型:false
type Test4 = IsArray<[number, string]>; // 类型:true(元组也是数组)

嵌套条件类型

条件类型可以嵌套,实现更复杂的类型判断:

typescript
// 嵌套条件类型
type TypeName<T> = 
  T extends string ? "string" :
  T extends number ? "number" :
  T extends boolean ? "boolean" :
  T extends undefined ? "undefined" :
  T extends null ? "null" :
  T extends Function ? "function" :
  "object";

// 使用示例
type Test1 = TypeName<string>;      // 类型:"string"
type Test2 = TypeName<number>;       // 类型:"number"
type Test3 = TypeName<boolean>;      // 类型:"boolean"
type Test4 = TypeName<undefined>;   // 类型:"undefined"
type Test5 = TypeName<null>;         // 类型:"null"
type Test6 = TypeName<() => void>;   // 类型:"function"
type Test7 = TypeName<{}>;           // 类型:"object"

分布式条件类型

当条件类型作用于联合类型时,TypeScript 会进行分布式条件类型(Distributive Conditional Types)处理:

分布式行为

typescript
// 条件类型作用于联合类型时,会分布到每个成员
type ToArray<T> = T extends any ? T[] : never;

// 使用示例
type Test = ToArray<string | number>;
// 等价于:ToArray<string> | ToArray<number>
// 结果:string[] | number[]

实际应用

typescript
// 提取函数类型的返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

// 使用示例
type Func1 = () => string;
type Func2 = () => number;
type Func3 = () => boolean;

type Return1 = ReturnType<Func1>;           // 类型:string
type Return2 = ReturnType<Func2>;             // 类型:number
type Return3 = ReturnType<Func3>;             // 类型:boolean
type Return4 = ReturnType<Func1 | Func2>;     // 类型:string | number(分布式)

阻止分布式行为

使用方括号包裹类型参数可以阻止分布式行为:

typescript
// 阻止分布式:使用 [T] 包裹
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;

// 对比
type Test1 = ToArray<string | number>;
// 类型:string[] | number[](分布式)

type Test2 = ToArrayNonDist<string | number>;
// 类型:(string | number)[](非分布式)

使用 infer 操作符

infer 操作符用于在条件类型中推断类型,它是条件类型最强大的特性之一:

基本用法

typescript
// 使用 infer 推断函数返回类型
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

// 使用示例
type Func = () => string;
type Return = GetReturnType<Func>;  // 类型:string

type AsyncFunc = () => Promise<number>;
type AsyncReturn = GetReturnType<AsyncFunc>;  // 类型:Promise<number>

推断函数参数类型

typescript
// 推断函数参数类型
type GetParameters<T> = T extends (...args: infer P) => any ? P : never;

// 使用示例
type Func1 = (a: string, b: number) => void;
type Params1 = GetParameters<Func1>;  // 类型:[string, number]

type Func2 = () => void;
type Params2 = GetParameters<Func2>;   // 类型:[]

推断数组元素类型

typescript
// 推断数组元素类型
type GetArrayElement<T> = T extends (infer E)[] ? E : never;

// 使用示例
type Test1 = GetArrayElement<string[]>;      // 类型:string
type Test2 = GetArrayElement<number[]>;      // 类型:number
type Test3 = GetArrayElement<[string, number]>; // 类型:string | number(元组)

推断 Promise 类型

typescript
// 推断 Promise 的解析类型
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

// 使用示例
type Test1 = UnwrapPromise<Promise<string>>;  // 类型:string
type Test2 = UnwrapPromise<Promise<number>>;  // 类型:number
type Test3 = UnwrapPromise<string>;            // 类型:string(非 Promise)

多个 infer 推断

typescript
// 同时推断多个类型
type GetFirstAndRest<T> = T extends [infer First, ...infer Rest] 
  ? { first: First; rest: Rest } 
  : never;

// 使用示例
type Test1 = GetFirstAndRest<[string, number, boolean]>;
// 类型:{ first: string; rest: [number, boolean] }

type Test2 = GetFirstAndRest<[string]>;
// 类型:{ first: string; rest: [] }

使用示例

示例 1:类型工具函数

typescript
// 提取函数返回类型
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

// 提取函数参数类型
type MyParameters<T> = T extends (...args: infer P) => any ? P : never;

// 提取构造函数参数类型
type ConstructorParameters<T> = T extends new (...args: infer P) => any ? P : never;

// 提取构造函数实例类型
type InstanceType<T> = T extends new (...args: any[]) => infer R ? R : never;

// 使用示例
function createUser(name: string, age: number): { name: string; age: number } {
  return { name, age };
}

type UserReturn = MyReturnType<typeof createUser>;
// 类型:{ name: string; age: number }

type UserParams = MyParameters<typeof createUser>;
// 类型:[string, number]

class User {
  constructor(public name: string, public age: number) {}
}

type UserConstructorParams = ConstructorParameters<typeof User>;
// 类型:[string, number]

type UserInstance = InstanceType<typeof User>;
// 类型:User

示例 2:类型过滤

typescript
// 过滤联合类型中的特定类型
type FilterString<T> = T extends string ? T : never;

// 使用示例
type Test = FilterString<string | number | boolean | string>;
// 类型:string(过滤掉非 string 类型)

// 过滤函数类型
type FilterFunction<T> = T extends Function ? T : never;

type Test2 = FilterFunction<string | number | (() => void) | boolean>;
// 类型:() => void

示例 3:类型转换

typescript
// 将联合类型转换为交叉类型
type UnionToIntersection<U> = 
  (U extends any ? (x: U) => void : never) extends (x: infer I) => void 
    ? I 
    : never;

// 使用示例
type Test = UnionToIntersection<{ a: string } | { b: number }>;
// 类型:{ a: string } & { b: number }

// 将可选属性变为必需属性
type RequiredKeys<T> = {
  [K in keyof T]-?: T[K];
};

// 将只读属性变为可写属性
type Writable<T> = {
  -readonly [K in keyof T]: T[K];
};

示例 4:深度解包 Promise

typescript
// 深度解包嵌套的 Promise
type DeepUnwrapPromise<T> = T extends Promise<infer U>
  ? DeepUnwrapPromise<U>
  : T;

// 使用示例
type Test1 = DeepUnwrapPromise<Promise<string>>;
// 类型:string

type Test2 = DeepUnwrapPromise<Promise<Promise<number>>>;
// 类型:number

type Test3 = DeepUnwrapPromise<Promise<Promise<Promise<boolean>>>>;
// 类型:boolean

示例 5:类型守卫工具

typescript
// 检查类型是否为 never
type IsNever<T> = [T] extends [never] ? true : false;

// 检查类型是否为 any
type IsAny<T> = 0 extends 1 & T ? true : false;

// 检查类型是否为 unknown
type IsUnknown<T> = IsNever<T> extends false
  ? IsAny<T> extends false
    ? unknown extends T
      ? true
      : false
    : false
  : false;

// 使用示例
type Test1 = IsNever<never>;      // 类型:true
type Test2 = IsNever<string>;      // 类型:false
type Test3 = IsAny<any>;           // 类型:true
type Test4 = IsAny<string>;        // 类型:false

示例 6:API 响应类型处理

typescript
// 根据状态类型选择不同的响应类型
type ApiResponse<T> = 
  T extends { status: "success" }
    ? { status: "success"; data: T["data"] }
    : T extends { status: "error" }
    ? { status: "error"; message: string }
    : never;

// 使用示例
type SuccessResponse = ApiResponse<{
  status: "success";
  data: { id: number; name: string };
}>;
// 类型:{ status: "success"; data: { id: number; name: string } }

type ErrorResponse = ApiResponse<{
  status: "error";
  message: string;
}>;
// 类型:{ status: "error"; message: string }

示例 7:数组操作类型

typescript
// 获取数组的第一个元素类型
type First<T extends any[]> = T extends [infer F, ...any[]] ? F : never;

// 获取数组的最后一个元素类型
type Last<T extends any[]> = T extends [...any[], infer L] ? L : never;

// 获取数组除第一个元素外的类型
type Tail<T extends any[]> = T extends [any, ...infer Rest] ? Rest : never;

// 获取数组除最后一个元素外的类型
type Head<T extends any[]> = T extends [...infer Rest, any] ? Rest : never;

// 使用示例
type Test1 = First<[string, number, boolean]>;  // 类型:string
type Test2 = Last<[string, number, boolean]>;    // 类型:boolean
type Test3 = Tail<[string, number, boolean]>;    // 类型:[number, boolean]
type Test4 = Head<[string, number, boolean]>;   // 类型:[string, number]

条件类型与工具类型

条件类型是构建 TypeScript 内置工具类型的基础:

Exclude 类型

typescript
// Exclude:从联合类型中排除特定类型
type MyExclude<T, U> = T extends U ? never : T;

// 使用示例
type Test = MyExclude<string | number | boolean, number>;
// 类型:string | boolean

Extract 类型

typescript
// Extract:从联合类型中提取特定类型
type MyExtract<T, U> = T extends U ? T : never;

// 使用示例
type Test = MyExtract<string | number | boolean, number | string>;
// 类型:string | number

NonNullable 类型

typescript
// NonNullable:排除 null 和 undefined
type MyNonNullable<T> = T extends null | undefined ? never : T;

// 使用示例
type Test = MyNonNullable<string | number | null | undefined>;
// 类型:string | number

类型检查示例

常见错误

typescript
// ❌ 错误:条件类型语法错误
// type Wrong = T extends U X : Y;  // 缺少 ? 和 :

// ❌ 错误:infer 只能在条件类型的 true 分支使用
// type Wrong<T> = T extends any ? infer R : never;  // infer 位置错误

// ❌ 错误:条件类型判断逻辑错误
type IsArray<T> = T extends any[] ? true : false;
type Test = IsArray<never>;  // 类型:never(分布式条件类型的行为)

正确写法

typescript
// ✅ 正确:基本条件类型
type IsString<T> = T extends string ? true : false;
type Test1 = IsString<string>;   // 类型:true
type Test2 = IsString<number>;   // 类型:false

// ✅ 正确:使用 infer 推断类型
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type Func = () => string;
type Return = GetReturnType<Func>;  // 类型:string

// ✅ 正确:阻止分布式行为
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;
type Test = ToArrayNonDist<string | number>;
// 类型:(string | number)[](非分布式)

// ✅ 正确:嵌套条件类型
type TypeName<T> = 
  T extends string ? "string" :
  T extends number ? "number" :
  "other";

注意事项

提示

  • 条件类型使用 extends 关键字检查类型关系,类似于类型约束
  • infer 操作符用于在条件类型中推断类型,只能在条件类型的 true 分支使用
  • 条件类型作用于联合类型时会进行分布式处理,每个成员都会单独判断
  • 使用方括号 [T] 包裹类型参数可以阻止分布式行为
  • 条件类型是构建复杂类型工具的基础,与泛型、映射类型结合使用更强大

注意

  • 条件类型的判断基于类型关系,不是值的关系
  • 分布式条件类型可能会产生意外的结果,需要理解其行为
  • 复杂的条件类型可能会影响类型检查性能
  • infer 操作符只能在条件类型的 true 分支使用,不能在 false 分支使用
  • 条件类型中的类型推断是延迟的,只有在实际使用时才会计算

重要

  • 条件类型是 TypeScript 类型级编程的核心特性,理解条件类型对于掌握高级类型非常重要
  • infer 操作符是条件类型最强大的特性,可以推断函数参数、返回类型、数组元素等
  • 分布式条件类型是条件类型的重要特性,理解其行为对于正确使用条件类型至关重要
  • 条件类型与泛型约束、映射类型、工具类型结合使用,可以构建强大的类型系统

信息

条件类型的优势

  • 类型级条件判断:在类型层面实现条件逻辑
  • 类型转换:将一种类型转换为另一种类型
  • 类型推断:通过 infer 推断复杂类型的组成部分
  • 类型工具构建:构建可复用的类型工具

条件类型的应用场景

  • 工具类型实现(Exclude、Extract、NonNullable 等)
  • 函数类型操作(参数类型、返回类型提取)
  • 类型转换和映射
  • 复杂类型系统的构建
  • 框架和库的类型定义

相关链接

基于 VitePress 构建