一、概述
条件类型(Conditional Types)是 TypeScript 中的一种高级类型,允许你根据类型之间的关系进行条件判断,并选择不同的类型。条件类型使得 TypeScript 类型系统更加灵活和强大,能够处理复杂的类型逻辑。本文将详细介绍条件类型的各种应用场景,并通过示例代码说明每个概念的使用方法。
二、什么是条件类型
条件类型使用语法 T extends U ? X : Y
来表示,其中 T
是待检查的类型,U
是要比较的类型,X
是当 T
满足 U
时返回的类型,Y
是当 T
不满足 U
时返回的类型。
示例
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
在上面的例子中,IsString
是一个条件类型,用于判断类型 T
是否是 string
。
使用场景
- 类型判断:当需要根据类型关系进行判断并选择不同的类型时,如检查类型是否是某个特定类型。
- 类型转换:当需要根据条件进行类型转换时,如将某个类型转换为另一个类型。
type IsNumber<T> = T extends number ? "Number" : "NotNumber";
type X = IsNumber<number>; // "Number"
type Y = IsNumber<string>; // "NotNumber"
三、条件类型与泛型
条件类型可以与泛型结合使用,创建更加灵活和通用的类型定义。
示例
type RemoveNull<T> = T extends null | undefined ? never : T;
type NonNullableString = RemoveNull<string | null | undefined>; // string
type NonNullableNumber = RemoveNull<number | null>; // number
在上面的例子中,RemoveNull
是一个条件类型,用于移除类型 T
中的 null
和 undefined
。
使用场景
- 泛型类型过滤:当需要对泛型类型进行过滤时,如移除泛型类型中的某些特定类型。
- 类型优化:当需要优化类型定义以提高类型安全性时,如移除不必要的类型。
type ExtractArrayType<T> = T extends (infer U)[] ? U : T;
type StringArrayElement = ExtractArrayType<string[]>; // string
type NumberArrayElement = ExtractArrayType<number[]>; // number
type NonArrayElement = ExtractArrayType<string>; // string
四、分布式条件类型
当条件类型作用于联合类型时,会对联合类型的每个成员进行独立的条件判断,这被称为分布式条件类型。
示例
type ToArray<T> = T extends any ? T[] : never;
type StrArr = ToArray<string>; // string[]
type NumArr = ToArray<number>; // number[]
type UnionArr = ToArray<string | number>; // string[] | number[]
在上面的例子中,ToArray
是一个分布式条件类型,将联合类型的每个成员转换为数组类型。
使用场景
- 联合类型转换:当需要对联合类型的每个成员进行转换时,如将联合类型的每个成员转换为数组类型。
- 类型操作简化:当需要简化对联合类型的操作时,如批量应用类型转换。
type Flatten<T> = T extends (infer U)[] ? U : T;
type Str = Flatten<string[]>; // string
type Num = Flatten<number[]>; // number
type NonArray = Flatten<string>; // string
五、条件类型的内置类型
TypeScript 提供了一些内置的条件类型,用于处理常见的类型操作。
Exclude<T, U>
Exclude
类型从类型 T
中排除所有可以赋值给类型 U
的类型。
type Excluded = Exclude<string | number | boolean, boolean>; // string | number
Extract<T, U>
Extract
类型从类型 T
中提取所有可以赋值给类型 U
的类型。
type Extracted = Extract<string | number | boolean, boolean>; // boolean
NonNullable<T>
NonNullable
类型移除类型 T
中的 null
和 undefined
。
type NonNull = NonNullable<string | number | undefined>; // string | number
ReturnType<T>
ReturnType
类型获取函数类型 T
的返回类型。
function exampleFunction() {
return { name: "Alice", age: 25 };
}
type ExampleReturnType = ReturnType<typeof exampleFunction>; // { name: string; age: number }
InstanceType<T>
InstanceType
类型获取构造函数类型 T
的实例类型。
class ExampleClass {
constructor(public name: string, public age: number) {}
}
type ExampleInstance = InstanceType<typeof ExampleClass>; // ExampleClass
六、自定义条件类型
你还可以创建自己的条件类型,以满足特定的需求。
示例:将联合类型转换为交叉类型
type UnionToIntersection<U> = (U extends any ? (x: U) => any : never) extends (x: infer R) => any ? R : never;
type U = { a: string } | { b: number } | { c: boolean };
type I = UnionToIntersection<U>; // { a: string } & { b: number } & { c: boolean }
使用场景
- 类型转换:当需要将联合类型转换为交叉类型时,如将多个类型的属性合并到一个新类型中。
- 复杂类型操作:当需要进行复杂的类型操作时,如自定义类型逻辑。
type IsNever<T> = [T] extends [never] ? true : false;
type A = IsNever<never>; // true
type B = IsNever<string>; // false
结论
条件类型是 TypeScript 中的重要特性,提供了灵活且强大的类型判断和转换方式。通过掌握条件类型与泛型、分布式条件类型、内置条件类型以及自定义条件类型的结合使用,你可以编写出更具通用性和可重用性的代码。在实际开发中,合理使用条件类型可以提高代码的类型安全性和可读性,帮助你更好地处理复杂的数据结构和类型操作。