TypeScript 2.8版本引入了条件类型(Conditional Types),TS条件类型可以进行类型选择,具体用法可以使用三元运算符实现,JS中的三元运算符用法一样,通过判断得到最终结果,TS条件类型最终得到的是数据类型。
条件类型
====
条件类型允许根据一个或多个条件对类型进行推断,并且还能在在类型级别上进行复杂的逻辑运算和类型操作。
一、基本用法
当T类型可以赋值给U类型时,则返回X类型,否则返回Y类型。
代码语言:javascript复制T extends U ? X : Y
列举例子如下:
其中TypeName
为条件类型,根据T
的具体类型返回不同类型的字符串,也就是字面量类型。
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 A = TypeName<'1'> //"string"
type B = TypeName<1> //"number"
type C = TypeName<true> //"blolean"
type D = TypeName<undefined> //"undefined"
type E = TypeName<()=>void> //"function"
type F = TypeName<{}> //"object "
当被检查类型T
为联合类型时:
type G = TypeName<'1'| 1 | true> // "string" | "number" | "boolean"
type H = TypeName<()=>void | {}> // "function" | "object"
二、分布式条件类型
在条件类型中,如果被检查的类型是一个 “裸” 类型参数,即没有被数组、元组或 Promise 等包装过,则该条件类型被称为分布式条件类型。==当分布式条件类型中被检查类型为联合类型,则在运算过程中分解多个分支== 。
代码语言:javascript复制type typeName<T> = T extends number ? "X" : "Y" ;
type H = typeName<string | number> // "X" | "Y"
//上面typeName在计算类型时,会分解为如下:
type H = string extends number ? "X" : "Y" | number extends number ? "X" : "Y"
= "X" | "Y"
三、非分布式条件类型
当T被数组、元组、Promise等包裹时,则运算过程中不会分解成多个分支,则该条件类型为非分布式条件类型。
代码语言:javascript复制type WrappedTuple<T> = [T] extends [boolean] ? "X" : "Y";
type WrappedArray<T> = T[] extends boolean[] ? "X" : "Y";
type WrappedPromise<T> = Promise<T> extends Promise<boolean> ? "X" : "Y";
type G = WrappedTuple<string | boolean>; // "Y"
type K = WrappedArray<string | boolean>; // "Y"
type L = WrappedPromise<string | boolean>; // "Y"
解析:
代码语言:javascript复制//由于计算数据时不会分解成多个分支
// [T] extends [boolean]
//WrappedTuple<string | boolean> 中 string | boolean 不是 boolean 类型,也不是其他原始类型
//T[] extends boolean[]
//WrappedArray<string | boolean> 中 string | boolean 不是 boolean 类型,也不是其他原始类型
//Promise<T> extends Promise<boolean>
//WrappedPromise<string | boolean> 中 string | boolean 不是 boolean 类型,也不是其他原始类型
四、结合范型使用
1.类型删除
在联合类型T
中删除联合类型U
中的成员,T类型中的剩余成员则组成新的类型。下面例子中,如果类型T
为类型U
的子类型,则返回never
,否则返回类型T
type Diff<T, U> = T extends U ? never : T;
type A = Diff<"a" | "b" | "c", "a" | "c" | "d">; // "b"
在联合类型T
中过滤出联合类型U
中的成员,过滤出来的成员则组成新的类型。下面例子中,如果类型T
为类型U
的子类型,则返回类型T
,否则返回never
2.过滤出公共类型
代码语言:javascript复制type Filter<T, U> = T extends U ? T : never;
type B = Filter<"a" | "b" | "c", "a" | "c" | "d"> ; // "a" | "c"
五、结合 infer 关键字
条件类型除了可以使用extends
,也可以使用infer
关键字。==infer 关键字可以用于从一个类型中提取另一个类型。==
1.获取函数返回类型
代码语言:javascript复制type FunctionReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
定义了FunctionReturnType
条件类型,它会检查类型T
是否为函数类型,如果是则通过infer
获取函数的返回值类型R
,否则返回never
类型。具体案例如下:
type FRT1 = FunctionReturnType<() => number> //number
type FRT2 = FunctionReturnType<(x: string, y: number) => string[]> //string
type FRT3 = FunctionReturnType<() => void>//void
type FRT4 = FunctionReturnType<() => {}>//{}
type FRT5 = FunctionReturnType<1>//never
六、结合keyof关键字
1.获取对象属性类型
keyof
主要是获取某个对象/类型的属性名来构成新类型。我们可以使用条件类型和 keyof 关键字来获取对象的属性。具体案例如下:
type PropertyType<T, K extends keyof T> = K extends keyof T ? T[K] : never;
上面代码定义了类型为PropertyType<T, K extends keyof T>
,通过检查K
是否是T
的一个属性名,如果是则返回该属性类型,否则返回never
。
type Obj = { a: string; b: number };
type A = PropertyType<Obj, "a">; // string
type B = PropertyType<Obj, "c">; // never
2.实现映射类型
映射类型是泛型类型的一种,可用于把原有的对象类型映射成新的对象类型。我们可以使用条件类型和 keyof
关键字来实现Partial
类型,Partial
类型是TS工具类之一。具体案例如下:
type Partial<T> = {
[K in keyof T]? : T[K]
}
定义类型Partial<T>
,遍历T
中所有属性,然后通过?
将所有属性变成可选属性。
type obj = {a:string,b:number}
type R = Partial<obj>//{a?:string,b?:number}
注意:Partial
是TS的工具类,所以声明Partial
是会报错的,可以换个标识符名称。
我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!