分布式条件类型
联合类型
联合类型代表了一组类型的可用集合,只要最终赋值的类型属于联合类型的成员之一,那么就可以认为它符合这个联合类型
属性互斥
通过多个对象类型的联合,来实现手动的互斥属性
interface IData {
info:
| {
isMan: true;
name: string;
}
| {
isMan: false;
name2: string;
};
}
declare const data: IData;
if (data.info.isMan) {
console.log(data.info.name);
} else {
console.log(data.info.name2);
}
后面是否需要加
|
,这里只是一种自己的规范,并不影响接口的功能使用
|
运算符可以更明确地表示一个属性可能具有多种类型的情况不使用
|
直接定义联合类型则更加简洁
分布式条件类型
分布式条件类型即联合类型的分支被单独的拿出来依次进行条件类型语句的判断
分布式条件类型要发生的前提条件:
-
需要是联合类型
-
联合类型得是通过泛型参数的形式传入
-
左边的泛型参数在条件类型语句中需要是裸类型参数,即没有被 [] 包裹
type test_1 = 1 | 2 | 3;
type test_2 = 1 | 2 | 4;
type test_3 = 1 | 2;
type test_4 = test_1 extends test_2 ? test_1 : never;//never,不满足 联合类型得是通过泛型参数的形式传入
type MyExtract_1<T, U> = [T] extends [U] ? T : never;
type MyExtract_2<T, U> = T extends U ? T : never;
type test_5 = MyExtract_1<test_1, test_2>;//never,不满足 泛型参数在条件类型语句中需要是裸类型参数,即没有被 [] 包裹
type test_6 = MyExtract_1<test_1, test_2>;//1 | 2
内部运行逻辑可以看成
type AExtractB = MyExtract_2<1 | 2 | 3, 1 | 2 | 4>; // 1 | 2
//==>
type _AExtractB =
| (1 extends 1 | 2 | 4 ? 1 : never) // 1
| (2 extends 1 | 2 | 4 ? 2 : never) // 2
| (3 extends 1 | 2 | 4 ? 3 : never); // never
当然对于一些特殊类型有着不一样的表现
- 如何判断一个类型是any类型
type IsAny<T> = T extends any ? true : false;
declare const test_1: IsAny<1>;//这样也会返回true
type FixIsAny<U> = (<T>() => T extends U ? 1 : 2) extends (<T>() => T extends any ? 1 : 2) ? true : false;
declare const test_2: FixIsAny<1>;//false
declare const test_3: FixIsAny<object>;//false
declare const test_4: FixIsAny<any>;//true
declare const test_5: FixIsAny<Function>;//false
declare const test_6: FixIsAny<unknown>;//false
- 如何判断两个类型相等
type IsEqual<T, U> = T extends U ? true : U extends T ? true : false;
declare const b_test_1: IsEqual<1, 1>;//true
declare const b_test_2: IsEqual<1, number>;//这样也会返回true
type FixIsEqual<T, U> = (<R>() => R extends U ? 1 : 2) extends (<R>() => R extends T ? 1 : 2) ? true : false;
declare const b_test_3: FixIsEqual<object, 1>;//false
declare const b_test_4: FixIsEqual<1, number>;//false
declare const b_test_5: FixIsEqual<1, any>;//false
declare const b_test_6: FixIsEqual<any, unknown>;//false
declare const b_test_7: FixIsEqual<any, any>;//true
declare const b_test_8: FixIsEqual<"", "">;//true
为什么通过 条件类型 extends 条件类型就可以实现我们想要的判断呢?
ts底层对于两个条件类型的处理有一些特殊
如果是两个条件类型 T1 extends U1 ? X1 : Y1
和 T2 extends U2 ? X2 : Y2
相关(也就是满足extends)的话,那 T1 和 T2 相关、X1 和 X2 相关、Y1 和 Y2 相关,而 U1 和 U2 相等。
但是需要注意,构建出的条件类型不能是已进行计算的,需要使用一种"延迟计算的条件类型"
使用
<T>()=>T
这种形式是为了引入泛型参数T,而T的具体类型未知,这会让条件类型变为延迟的条件类型,也就是在给T泛型赋予类型时才会计算,而两个"延迟的条件类型"进行相关推导时才会满足上面的关系
- 如何判断是联合类型
type ShowUnion<T, R = T> = T extends R ? { T: R; } : never;
type IsUnionTest = [1 | 2] extends [1 | object] ? true : false;//false
type IsUnionTest2 = [1 | 2] extends [1 | object | 2] ? true : false;//true
type IsUnionTest3 = [1 | 2] extends [any] ? true : false;//true
type IsUnionTest4 = [1 | 2] extends [unknown] ? true : false;//true
type IsUnionTest5 = [1 | 2] extends [never] ? true : false;//false
type IsUnion<T, R = T> = T extends R ? [R] extends [T] ? false : true : false;
declare const c_test_1: ShowUnion<1 | any | unknown | string>;//only any : {T: any;}
declare const c_test_2: IsUnion<1 | any | unknown | string>;// ==> IsUnion<any> 所以为false
declare const c_test_3: IsUnion<any>;//false
declare const c_test_4: IsUnion<object | 1>;//true
4.如何判断是否为never
type IsNever<T> = T extends never ? true : false;
declare const d_test_1: IsNever<never>;//never
declare const d_test_2: IsNever<string>;// false
declare const d_test_3: IsNever<any>;//boolean
declare const d_test_4: IsNever<object | 1>;//false
type FixIsNever<T> = [T] extends [never] ? true : false;
declare const d_test_5: FixIsNever<never>;//true
declare const d_test_6: FixIsNever<string>;// false
declare const d_test_7: FixIsNever<any>;//false
declare const d_test_8: FixIsNever<object | 1>;//false
为什么d_test_1
和d_test_3
返回的类型时超出预期的?
ts对于never和any在条件类型中有着特殊的处理,如果条件类型左边是类型参数,并且传入的是 never,那么直接返回 never:如果传入的是any,那么会将trueType和falseType合并返回(boolean其实是true和false的联合类型)
5.如何判断是否为元组
元组的length为数字,而数组的length为number
type IsTuple<T> = T extends [...infer A] ? FixIsEqual<A['length'], number> extends true ? false : true : false;
declare const e_test_1: IsTuple<never>;//never
declare const e_test_2: IsTuple<string>;// false
declare const e_test_3: IsTuple<any[]>;//false
declare const e_test_4: IsTuple<object | 1>;//false
declare const e_test_5: IsTuple<any>;//false
declare const e_test_6: IsTuple<[object | 1]>;//true
declare const e_test_7: IsTuple<[1, object]>;//true
declare const e_test_8: IsTuple<[any, object]>;//true
type FixIsTuple<T> = FixIsNever<T> extends true ? false : T extends [...infer A] ? FixIsEqual<A['length'], number> extends true ? false : true : false;
declare const e_test_9: FixIsTuple<never>;//false