Featured image of post TS 类型系统中的分支:条件类型简介Featured image of post TS 类型系统中的分支:条件类型简介

TS 类型系统中的分支:条件类型简介

创建类型逻辑,根据使用条件类型约束的检查来解析输出结构。

介绍

TypeScript 的类型系统是图灵完备,而释放其全部功能的关键功能之一是条件类型。就像 JavaScript 中的三元运算符 (condition ? a : b) 一样,条件类型可让您创建类型级逻辑:T extends U ? X : Y。这使得动态的、上下文感知的类型解析成为可能。


基本语法

条件类型检查一个类型是否扩展另一个类型:

type IsString<T> = T extends string ? true : false;

type A = IsString<'hello'>; // true
type B = IsString<42>;      // false

条件 T extends U 询问:“T 是否可以分配给 U?”如果是,则解析到true分支;否则,为假分支。


infer 关键字

infer 关键字允许您从条件中提取类型:

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function greet() { return 'hello'; }
type G = ReturnType<typeof greet>; // string

infer R 声明一个从匹配位置推断出的类型变量 R。这是许多实用程序类型的基础。


分配条件类型

当条件类型与 union 一起使用时,它会分布在每个成员上:

type ToArray<T> = T extends any ? T[] : never;

type Result = ToArray<string | number>;
// string[] | number[]  (not (string | number)[])

除非您将两侧包裹起来,否则这种分布会自动发生:

type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;

type Result2 = ToArrayNonDist<string | number>;
// (string | number)[]

使用条件的内置实用程序类型

许多标准实用程序类型依赖于条件类型:

// Exclude — removes types from a union
type T0 = Exclude<'a' | 'b' | 'c', 'a'>;
// 'b' | 'c'

// Extract — keeps only matching types
type T1 = Extract<string | number | (() => void), Function>;
// () => void

// NonNullable — removes null and undefined
type T2 = NonNullable<string | number | null | undefined>;
// string | number

以下是 Exclude 的实现方式:

type MyExclude<T, U> = T extends U ? never : T;

现实世界的模式

基于输入的条件返回类型

type AsyncResult<T> = T extends Promise<infer U> ? U : T;

async function fetchData(): Promise<string> {
  return 'data';
}

type Data = AsyncResult<ReturnType<typeof fetchData>>; // string

函数重载 无重载

type StringOrNumber<T> = T extends string ? string : number;

function process<T extends string | number>(val: T): StringOrNumber<T>;
function process(val: any): any {
  return typeof val === 'string' ? val.toUpperCase() : val * 10;
}

const a = process('hello'); // string
const b = process(42);      // number

深层条件类型示例

type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object
    ? DeepReadonly<T[K]>
    : T[K];
};

interface User {
  name: string;
  address: { city: string; zip: number };
}

type ReadonlyUser = DeepReadonly<User>;
// { readonly name: string; readonly address: { readonly city: string; readonly zip: number } }

结论

条件类型是类型系统的 if/else。与 infer、分布和映射类型相结合,它们可以实现复杂的类型转换,从而在编译时捕获错误。掌握条件来编写真正类型安全、自记录的 TypeScript 代码。