# 条件类型 (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[] }# 预定义条件类型
# 知识点
T extends U ? X : Y表示条件类型。当类型T扩展自U时去类型X, 否则取Y- 采用类似
T extends U ? X : never或T extends U ? never: X来进行条件类型约束
# 延伸阅读
- Conditional types (opens new window)
- Type inference in conditional types (opens new window)
- [ ] Conditional Types (opens new window)
- [ ] Conditional types in TypeScript (opens new window)
- [ ] https://www.typescriptlang.org/docs/handbook/2/conditional-types.html