@@ -925,3 +925,278 @@ let originalProps = unproxify(proxyProps);
925925
926926注意这个拆包推断只适用于同态的映射类型。
927927如果映射类型不是同态的,那么需要给拆包函数一个明确的类型参数。
928+
929+ ## 有条件类型
930+
931+ TypeScript 2.8引入了* 有条件类型* ,它能够表示非统一的类型。
932+ 有条件的类型会以一个条件表达式进行类型关系检测,从而在两种类型中选择其一:
933+
934+ ``` ts
935+ T extends U ? X : Y
936+ ```
937+
938+ 上面的类型意思是,若` T ` 能够赋值给` U ` ,那么类型是` X ` ,否则为` Y ` 。
939+
940+ 有条件的类型` T extends U ? X : Y ` 或者* 解析* 为` X ` ,或者* 解析* 为` Y ` ,再或者* 延迟* 解析,因为它可能依赖一个或多个类型变量。
941+ 若` T ` 或` U ` 包含类型参数,那么是否解析为` X ` 或` Y ` 或推迟,取决于类型系统是否有足够的信息来确定` T ` 总是可以赋值给` U ` 。
942+
943+ 下面是一些类型可以被立即解析的例子:
944+
945+ ``` ts
946+ declare function f<T extends boolean >(x : T ): T extends true ? string : number ;
947+
948+ // Type is 'string | number
949+ let x = f (Math .random () < 0.5 )
950+
951+ ```
952+
953+ 另外一个例子涉及` TypeName ` 类型别名,它使用了嵌套了有条件类型:
954+
955+ ``` ts
956+ type TypeName <T > =
957+ T extends string ? " string" :
958+ T extends number ? " number" :
959+ T extends boolean ? " boolean" :
960+ T extends undefined ? " undefined" :
961+ T extends Function ? " function" :
962+ " object" ;
963+
964+ type T0 = TypeName <string >; // "string"
965+ type T1 = TypeName <" a" >; // "string"
966+ type T2 = TypeName <true >; // "boolean"
967+ type T3 = TypeName <() => void >; // "function"
968+ type T4 = TypeName <string []>; // "object"
969+ ```
970+
971+ 下面是一个有条件类型被推迟解析的例子:
972+
973+ ``` ts
974+ interface Foo {
975+ propA: boolean ;
976+ propB: boolean ;
977+ }
978+
979+ declare function f<T >(x : T ): T extends Foo ? string : number ;
980+
981+ function foo<U >(x : U ) {
982+ // Has type 'U extends Foo ? string : number'
983+ let a = f (x );
984+
985+ // This assignment is allowed though!
986+ let b: string | number = a ;
987+ }
988+ ```
989+
990+ 这里,` a ` 变量含有未确定的有条件类型。
991+ 当有另一段代码调用` foo ` ,它会用其它类型替换` U ` ,TypeScript将重新计算有条件类型,决定它是否可以选择一个分支。
992+
993+ 与此同时,我们可以将有条件类型赋值给其它类型,只要有条件类型的每个分支都可以赋值给目标类型。
994+ 因此在我们的例子里,我们可以将` U extends Foo ? string : number ` 赋值给` string | number ` ,因为不管这个有条件类型最终结果是什么,它只能是` string ` 或` number ` 。
995+
996+ ### 分布式有条件类型
997+
998+ 如果有条件类型里待检查的类型是` naked type parameter ` ,那么它也被称为“分布式有条件类型”。
999+ 分布式有条件类型在实例化时会自动分发成联合类型。
1000+ 例如,实例化` T extends U ? X : Y ` ,` T ` 的类型为` A | B | C ` ,会被解析为` (A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y) ` 。
1001+
1002+ #### 例子
1003+
1004+ ``` ts
1005+ type T10 = TypeName <string | (() => void )>; // "string" | "function"
1006+ type T12 = TypeName <string | string [] | undefined >; // "string" | "object" | "undefined"
1007+ type T11 = TypeName <string [] | number []>; // "object"
1008+ ```
1009+
1010+ 在` T extends U ? X : Y ` 的实例化里,对` T ` 的引用被解析为联合类型的一部分(比如,` T ` 指向某一单个部分,在有条件类型分布到联合类型之后)。
1011+ 此外,在` X ` 内对` T ` 的引用有一个附加的类型参数约束` U ` (例如,` T ` 被当成在` X ` 内可赋值给` U ` )。
1012+
1013+ #### 例子
1014+
1015+ ``` ts
1016+ type BoxedValue <T > = { value: T };
1017+ type BoxedArray <T > = { array: T [] };
1018+ type Boxed <T > = T extends any [] ? BoxedArray <T [number ]> : BoxedValue <T >;
1019+
1020+ type T20 = Boxed <string >; // BoxedValue<string>;
1021+ type T21 = Boxed <number []>; // BoxedArray<number>;
1022+ type T22 = Boxed <string | number []>; // BoxedValue<string> | BoxedArray<number>;
1023+ ```
1024+
1025+ 注意在` Boxed<T> ` 的` true ` 分支里,` T ` 有个额外的约束` any[] ` ,因此它适用于` T[number] ` 数组元素类型。同时也注意一下有条件类型是如何分布成联合类型的。
1026+
1027+ 有条件类型的分布式的属性可以方便地用来* 过滤* 联合类型:
1028+
1029+ ``` ts
1030+ type Diff <T , U > = T extends U ? never : T ; // Remove types from T that are assignable to U
1031+ type Filter <T , U > = T extends U ? T : never ; // Remove types from T that are not assignable to U
1032+
1033+ type T30 = Diff <" a" | " b" | " c" | " d" , " a" | " c" | " f" >; // "b" | "d"
1034+ type T31 = Filter <" a" | " b" | " c" | " d" , " a" | " c" | " f" >; // "a" | "c"
1035+ type T32 = Diff <string | number | (() => void ), Function >; // string | number
1036+ type T33 = Filter <string | number | (() => void ), Function >; // () => void
1037+
1038+ type NonNullable <T > = Diff <T , null | undefined >; // Remove null and undefined from T
1039+
1040+ type T34 = NonNullable <string | number | undefined >; // string | number
1041+ type T35 = NonNullable <string | string [] | null | undefined >; // string | string[]
1042+
1043+ function f1<T >(x : T , y : NonNullable <T >) {
1044+ x = y ; // Ok
1045+ y = x ; // Error
1046+ }
1047+
1048+ function f2<T extends string | undefined >(x : T , y : NonNullable <T >) {
1049+ x = y ; // Ok
1050+ y = x ; // Error
1051+ let s1: string = x ; // Error
1052+ let s2: string = y ; // Ok
1053+ }
1054+ ```
1055+
1056+ 有条件类型与映射类型结合时特别有用:
1057+
1058+ ``` ts
1059+ type FunctionPropertyNames <T > = { [K in keyof T ]: T [K ] extends Function ? K : never }[keyof T ];
1060+ type FunctionProperties <T > = Pick <T , FunctionPropertyNames <T >>;
1061+
1062+ type NonFunctionPropertyNames <T > = { [K in keyof T ]: T [K ] extends Function ? never : K }[keyof T ];
1063+ type NonFunctionProperties <T > = Pick <T , NonFunctionPropertyNames <T >>;
1064+
1065+ interface Part {
1066+ id: number ;
1067+ name: string ;
1068+ subparts: Part [];
1069+ updatePart(newName : string ): void ;
1070+ }
1071+
1072+ type T40 = FunctionPropertyNames <Part >; // "updatePart"
1073+ type T41 = NonFunctionPropertyNames <Part >; // "id" | "name" | "subparts"
1074+ type T42 = FunctionProperties <Part >; // { updatePart(newName: string): void }
1075+ type T43 = NonFunctionProperties <Part >; // { id: number, name: string, subparts: Part[] }
1076+ ```
1077+
1078+ 与联合类型和交叉类型相似,有条件类型不允许递归地引用自己。比如下面的错误。
1079+
1080+ #### 例子
1081+
1082+ ``` ts
1083+ type ElementType <T > = T extends any [] ? ElementType <T [number ]> : T ; // Error
1084+ ```
1085+
1086+ ### 有条件类型中的类型推断
1087+
1088+ 现在在有条件类型的` extends ` 子语句中,允许出现` infer ` 声明,它会引入一个待推断的类型变量。
1089+ 这个推断的类型变量可以在有条件类型的true分支中被引用。
1090+ 允许出现多个同类型变量的` infer ` 。
1091+
1092+ 例如,下面代码会提取函数类型的返回值类型:
1093+
1094+ ``` ts
1095+ type ReturnType <T > = T extends (... args : any []) => infer R ? R : any ;
1096+ ```
1097+
1098+ 有条件类型可以嵌套来构成一系列的匹配模式,按顺序进行求值:
1099+
1100+ ``` ts
1101+ type Unpacked <T > =
1102+ T extends (infer U )[] ? U :
1103+ T extends (... args : any []) => infer U ? U :
1104+ T extends Promise <infer U > ? U :
1105+ T ;
1106+
1107+ type T0 = Unpacked <string >; // string
1108+ type T1 = Unpacked <string []>; // string
1109+ type T2 = Unpacked <() => string >; // string
1110+ type T3 = Unpacked <Promise <string >>; // string
1111+ type T4 = Unpacked <Promise <string >[]>; // Promise<string>
1112+ type T5 = Unpacked <Unpacked <Promise <string >[]>>; // string
1113+ ```
1114+
1115+ 下面的例子解释了在协变位置上,同一个类型变量的多个候选类型会被推断为联合类型:
1116+
1117+ ``` ts
1118+ type Foo <T > = T extends { a: infer U , b: infer U } ? U : never ;
1119+ type T10 = Foo <{ a: string , b: string }>; // string
1120+ type T11 = Foo <{ a: string , b: number }>; // string | number
1121+ ```
1122+
1123+ 相似地,在抗变位置上,同一个类型变量的多个候选类型会被推断为交叉类型:
1124+
1125+ ``` ts
1126+ type Bar <T > = T extends { a: (x : infer U ) => void , b: (x : infer U ) => void } ? U : never ;
1127+ type T20 = Bar <{ a: (x : string ) => void , b: (x : string ) => void }>; // string
1128+ type T21 = Bar <{ a: (x : string ) => void , b: (x : number ) => void }>; // string & number
1129+ ```
1130+
1131+ 当推断具有多个调用签名(例如函数重载类型)的类型时,用* 最后* 的签名(大概是最自由的包含所有情况的签名)进行推断。
1132+ 无法根据参数类型列表来解析重载。
1133+
1134+ ``` ts
1135+ declare function foo(x : string ): number ;
1136+ declare function foo(x : number ): string ;
1137+ declare function foo(x : string | number ): string | number ;
1138+ type T30 = ReturnType <typeof foo >; // string | number
1139+ ```
1140+
1141+ 无法在正常类型参数的约束子语句中使用` infer ` 声明:
1142+
1143+ ``` ts
1144+ type ReturnType <T extends (... args : any []) => infer R > = R ; // 错误,不支持
1145+ ```
1146+
1147+ 但是,可以这样达到同样的效果,在约束里删掉类型变量,用有条件类型替换:
1148+
1149+ ``` ts
1150+ type AnyFunction = (... args : any []) => any ;
1151+ type ReturnType <T extends AnyFunction > = T extends (... args : any []) => infer R ? R : any ;
1152+ ```
1153+
1154+ ### 预定义的有条件类型
1155+
1156+ TypeScript 2.8在` lib.d.ts ` 里增加了一些预定义的有条件类型:
1157+
1158+ * ` Exclude<T, U> ` -- 从` T ` 中剔除可以赋值给` U ` 的类型。
1159+ * ` Extract<T, U> ` -- 提取` T ` 中可以赋值给` U ` 的类型。
1160+ * ` NonNullable<T> ` -- 从` T ` 中剔除` null ` 和` undefined ` 。
1161+ * ` ReturnType<T> ` -- 获取函数返回值类型。
1162+ * ` InstanceType<T> ` -- 获取构造函数类型的实例类型。
1163+
1164+ #### Example
1165+
1166+ ``` ts
1167+ type T00 = Exclude <" a" | " b" | " c" | " d" , " a" | " c" | " f" >; // "b" | "d"
1168+ type T01 = Extract <" a" | " b" | " c" | " d" , " a" | " c" | " f" >; // "a" | "c"
1169+
1170+ type T02 = Exclude <string | number | (() => void ), Function >; // string | number
1171+ type T03 = Extract <string | number | (() => void ), Function >; // () => void
1172+
1173+ type T04 = NonNullable <string | number | undefined >; // string | number
1174+ type T05 = NonNullable <(() => string ) | string [] | null | undefined >; // (() => string) | string[]
1175+
1176+ function f1(s : string ) {
1177+ return { a: 1 , b: s };
1178+ }
1179+
1180+ class C {
1181+ x = 0 ;
1182+ y = 0 ;
1183+ }
1184+
1185+ type T10 = ReturnType <() => string >; // string
1186+ type T11 = ReturnType <(s : string ) => void >; // void
1187+ type T12 = ReturnType <(<T >() => T )>; // {}
1188+ type T13 = ReturnType <(<T extends U , U extends number []>() => T )>; // number[]
1189+ type T14 = ReturnType <typeof f1 >; // { a: number, b: string }
1190+ type T15 = ReturnType <any >; // any
1191+ type T16 = ReturnType <never >; // any
1192+ type T17 = ReturnType <string >; // Error
1193+ type T18 = ReturnType <Function >; // Error
1194+
1195+ type T20 = InstanceType <typeof C >; // C
1196+ type T21 = InstanceType <any >; // any
1197+ type T22 = InstanceType <never >; // any
1198+ type T23 = InstanceType <string >; // Error
1199+ type T24 = InstanceType <Function >; // Error
1200+ ```
1201+
1202+ > 注意:` Exclude ` 类型是[ 建议的] ( https://github.com/Microsoft/TypeScript/issues/12215#issuecomment-307871458 ) ` Diff ` 类型的一种实现。我们使用` Exclude ` 这个名字是为了避免破坏已经定义了` Diff ` 的代码,并且我们感觉这个名字能更好地表达类型的语义。我们没有增加` Omit<T, K> ` 类型,因为它可以很容易的用` Pick<T, Exclude<keyof T, K>> ` 来表示。
0 commit comments