# 条件类型 (conditional type)

TypeScript 借鉴 JavaScript 条件运算符 (opens new window) 语法实现了条件类型。

# 条件类型表达式

在 泛型约束 章节,可以通过 T extends U 的方式来限制泛型 T 扩展自类型 U。 条件类型语句采用 T extends U ? X :Y 表示,如果类型 T 扩展自类型 U 则返回类型为 X ,否则为 Y 。

// 判断字面量类型 'foo' 是否扩展与 string 类型
// 是则返回 true 字面量类型,否则返回 false 字面量类型
type IsString = 'foo' extends string? true : false;

可以组合多个条件类型表达式,来处理复杂类型判断

// 效果类似 js 嵌套多个条件表达式
type TypeName<T> = T extends string
  ? 'string'
  : T extends number
  ? 'number'
  : T extends boolean
  ? 'boolean'
  : T extends undefined
  ? 'undefined'
  : T extends Function
  ? 'function'
  : 'object';

type T0 = TypeName<string>; // "string"
type T1 = TypeName<'a'>; // "string"
type T2 = TypeName<true>; // "boolean"
type T3 = TypeName<() => void>; // "function"
type T4 = TypeName<string[]>; // "object"

# 条件类型约束

有时我们只关注条件类型表达式单一分支对应的类型,来实现对类型 T 的约束或过滤。 此时可采用 T extends U? X : never 或 T extends U? never: Y 的语法来描述。

// 从类型 T 中移除扩展自 U 的类型
type Diff<T, U> = T extends U ? never : T;  

// 过滤扩展自 U 的类型 T
type Filter<T, U> = T extends U ? T : never;  

// "b" | "d"
type T30 = Diff<"a" | "b" | "c" | "d", "a" | "c" | "f">;  
// "a" | "c"
type T31 = Filter<"a" | "b" | "c" | "d", "a" | "c" | "f">;  
// string | number
type T32 = Diff<string | number | (() => void), Function>; 
 
 // () => void
type T33 = Filter<string | number | (() => void), Function>; 

TIP

注意这里的 never 用于显示说明此分支类型不可用。

# 条件类型推断

在 T extends U? X : Y 中,可以在 U 中使用类似 infer V 的语法表示推断 U 类型的某部分为 V,然后将 V 作为类型传递給 X 或 Y。

提取函数返回值类型

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

// 返回 string 类型
type str = ReturnFuncType<() => string>
// 返回 number 类型
type num = ReturnFuncType<() => number>

提取类数组元素的子项类型

type FlattenArr<T> = T extends Array<infer U> ? U : T;

// 返回 string
type str = FlattenArr<string[]>

interface C1 {
  a: 1
}
// 返回 C1
type C1Type = FlattenArr<C1[]>

# infer 详解

infer 只能在条件类型的 extends 子句中使用,从复杂类型中推断出某个部分作为新类型。

// infer 只能在条件类型的 extends 子句中使用
// 用于从类型中推断出某个部分作为新类型

// 1. 推断函数返回值类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// 示例
function asyncFunction() {
  return Promise.resolve(42);
}
type AsyncReturn = ReturnType<typeof asyncFunction>; // Promise<number>

// 2. 推断函数参数类型
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
// 示例
function greet(name: string, age: number) {}
type GreetParams = Parameters<typeof greet>; // [string, number]

// 3. 推断构造函数参数
type ConstructorParameters<T> = T extends new (...args: infer P) => any ? P : never;
class Person {
  constructor(public name: string, public age: number) {}
}
type PersonParams = ConstructorParameters<typeof Person>; // [string, number]

// 4. 从数组/元组类型推断元素类型
type ElementType<T> = T extends (infer E)[] ? E : never;
type Num = ElementType<number[]>; // number

// 5. 推断 Promise 内部类型
type Awaited<T> = T extends Promise<infer U> ? U : T;
type PromiseResult = Awaited<Promise<string>>; // string

// 6. 多重 infer
type ExtractArgs<T> = T extends `${infer _name}(${infer Args})` ? Args : never;
type Args1 = ExtractArgs<"add(1, 2)">; // "1, 2"
type Args2 = ExtractArgs<"multiply(3, 4)">; // "3, 4"

// 7. infer 在元组提取中的应用
type First<T extends any[]> = T extends [infer F, ...any[]] ? F : never;
type FirstNum = First<[1, 2, 3]>; // 1
type FirstStr = First<["a", "b"]>; // "a"

// 8. 推断对象属性类型
type PropType<T, K extends keyof T> = T extends { [P in K]: infer V } ? V : never;
interface User {
  name: string;
  age: number;
}
type UserName = PropType<User, "name">; // string

// 9. 递归 infer - 提取嵌套类型
type DeepAwaited<T> = T extends Promise<infer U>
  ? DeepAwaited<U>
  : T;
// 等待嵌套 Promise
type Nested = DeepAwaited<Promise<Promise<string>>>; // string

// 10. infer 与 never 联合使用
type UnwrapArray<T> = T extends Array<infer U> ? U : never;
type StrOrNum = UnwrapArray<string[] | number[]>; // string | number

常见 infer 场景:

  • ReturnType<T> — 提取函数返回值
  • Parameters<T> — 提取函数参数
  • ConstructorParameters<T> — 提取构造函数参数
  • Awaited<T> — 提取 Promise 内部类型
  • 递归 infer — 展平嵌套类型
// infer 只能在条件类型的 extends 子句中使用
// 用于从类型中推断出某个部分作为新类型

// 1. 推断函数返回值类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// 示例
function asyncFunction() {
  return Promise.resolve(42);
}
type AsyncReturn = ReturnType<typeof asyncFunction>; // Promise<number>

// 2. 推断函数参数类型
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
// 示例
function greet(name: string, age: number) {}
type GreetParams = Parameters<typeof greet>; // [string, number]

// 3. 推断构造函数参数
type ConstructorParameters<T> = T extends new (...args: infer P) => any ? P : never;
class Person {
  constructor(public name: string, public age: number) {}
}
type PersonParams = ConstructorParameters<typeof Person>; // [string, number]

// 4. 从数组/元组类型推断元素类型
type ElementType<T> = T extends (infer E)[] ? E : never;
type Num = ElementType<number[]>; // number

// 5. 推断 Promise 内部类型
type Awaited<T> = T extends Promise<infer U> ? U : T;
type PromiseResult = Awaited<Promise<string>>; // string

// 6. 多重 infer
type ExtractArgs<T> = T extends `${infer _name}(${infer Args})` ? Args : never;
type Args1 = ExtractArgs<"add(1, 2)">; // "1, 2"
type Args2 = ExtractArgs<"multiply(3, 4)">; // "3, 4"

// 7. infer 在元组提取中的应用
type First<T extends any[]> = T extends [infer F, ...any[]] ? F : never;
type FirstNum = First<[1, 2, 3]>; // 1
type FirstStr = First<["a", "b"]>; // "a"

// 8. 推断对象属性类型
type PropType<T, K extends keyof T> = T extends { [P in K]: infer V } ? V : never;
interface User {
  name: string;
  age: number;
}
type UserName = PropType<User, "name">; // string

// 9. 递归 infer - 提取嵌套类型
type DeepAwaited<T> = T extends Promise<infer U>
  ? DeepAwaited<U>
  : T;
// 等待嵌套 Promise
type Nested = DeepAwaited<Promise<Promise<string>>>; // string

// 10. infer 与 never 联合使用
type UnwrapArray<T> = T extends Array<infer U> ? U : never;
type StrOrNum = UnwrapArray<string[] | number[]>; // string | number

# 结合联合类型

采用 T extends U? X :Y 进行条件类型判断时,若类型 T 为联合类型 A | B | C 则此时条件判断的结果会拆分为 (A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)。

type TypeName<T> = T extends string
  ? 'string'
  : T extends number
  ? 'number'
  : T extends boolean
  ? 'boolean'
  : T extends undefined
  ? 'undefined'
  : T extends Function
  ? 'function'
  : 'object';

// "string" | "function"
type TD0 = TypeName<string | (() => void)>;
// "string" | "object" | "undefined"
type TD1 = TypeName<string | string[] | undefined>;
// "object"
type TD2 = TypeName<string[] | number[]>;

这种类型也被称为 分配条件类型(Distributive Conditional Types)

有时我们期望改变分配条件类型的特性,可以采用形如 [T] extends [U] ? X : Y 处理。

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

// 返回 string[] | number[]
type Bar = Foo12<string | number>;

// 期望单独结构各类型改写为如下形式
type Foo13<T> = [T] extends [any] ? T[] : never;

// 返回 (string | number)[]
type Bar1 = Foo13<string | number>;

当 U 为联合类型时,相当于多个逻辑条件取或, T extends A | B | C? X :Y 等同于 T extends A、 T extends B、T extends C 满足其中之一即可。

type IsLiteral<T> = T extends string | number | boolean  ? true : false;

// 是字面量类型发挥 true
type T01 = IsLiteral<1>
type T02 = IsLiteral<'adf'>
type T03 = IsLiteral<true>

// 非字面量类型返回 fasle
type T04 = IsLiteral<{a: string}>

# 结合映射类型

条件类型,结合映射类型可以很方便的进行过滤

type FunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T];
type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>;

type NonFunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T];
type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;

interface Part {
    id: number;
    name: string;
    subparts: Part[];
    updatePart(newName: string): void;
}

type T40 = FunctionPropertyNames<Part>;  // "updatePart"
type T41 = NonFunctionPropertyNames<Part>;  // "id" | "name" | "subparts"
type T42 = FunctionProperties<Part>;  // { updatePart(newName: string): void }
type T43 = NonFunctionProperties<Part>;  // { id: number, name: string, subparts: Part[] }

# 预定义条件类型

# 知识点

  1. T extends U ? X : Y 表示条件类型。当类型 T 扩展自 U 时去类型 X, 否则取 Y
  2. 采用类似 T extends U ? X : never 或 T extends U ? never: X 来进行条件类型约束

# 延伸阅读