Condition Type(条件类型)
ts中条件类型有点类似三元运算符,如
interface BasicType {
basic: string
}
interface DerivedType extends BasicType{
derived: string
}
type ConditionType = DerivedType extends BasicType ? boolean : string
上面代码CondtionType即条件类型,表达式 类型A extends 类型B
结果为true 则ConditionType为boolean类型,否则为string类型。
Condition Type应用场景
假设现在我们有一个方法,允许我们传入number或者string类型的参数,根据不同的入参,可以得到不同的返回,如
type IdLabel = {
id: number
}
type NameLabel = {
name: string
}
function getLabel(id: number):IdLabel;
function getLabel(name: string):NameLabel;
function getLabel(idOrName: number | string): IdLabel | NameLabel;
function getLabel(idOrName: number | string): IdLabel | NameLabel {
if(typeof idOrName === 'string'){
return {
name: "name"
}
}else{
return {
id: 1
}
}
}
let l1 = getLabel(1) // IdLabel类型
let l11 =getLabel('1') // NameLabel类型
从上面代码可看到,使用重载的方式会使得代码量很冗长,此时我们可以通过条件类型类简化代码,如
type IdOrName<T extends number | string> = T extends number ? IdLabel : NameLabel;
function getLabel2<T extends number | string>(idOrName: T): IdOrName<T>{
if(idOrName === 1){
return {
id: 1
} as IdOrName<T>
}else{
return {
name: "2"
} as IdOrName<T>
}
}
我们定义了条件类型IdOrName,其接收一个泛型T,其可以是number或string类型,当其为number类型时,IdOrName为IdLabel类型,否则为NameLabel类型。
Condition Type与 Indexed Access Type一起使用
- 获取任意对象类型message属性的类型
type MessageType = {
message: string
}
type MessageOf<T extends { message: unknown }> = T["message"]
type Email = {
message: string
}
type WithoutMessage = {
name:string
}
type mt = MessageOf<Email> // mt类型为string
type mt1 = MessageOf<WithoutMessage> //Property 'message' is missing in type 'WithoutMessage' but required in type '{ message: unknown; }'
假如我们想当某对象类型没有message属性时不是报错,而是当某对象类型没有message属性时,新建立的类型为never,我们可以这样定义:
type MessageOf<T> = T extends { message: unknown } ? T['message'] : never
- 当传递过来的类型是数组类型,则获取其所包含的对象类型,否则返回传递过来的类型
type GetObjType<T> = T extends any[] ? T[number] : T
infer与Condition Type
上面的例子使用Condition Type都是从已知类型获取类型,比如type MessageOf<T extends { message: unknown }> = T["message"]
。我们已知传递过来的对象具备message属性。但假如我们不清楚传递过来的对象数据结构,又想获取其内部的某些数据类型呢?此时我们可以使用infer关键字,定义一个“新的泛型”,让ts自动推断传递过来的数据类型。看如下代码
type GetObjTypeFromArr<Type> = Type extends Array<infer Item> ? Item : never
let arr = [1,2,'3',true]
type ArrType = GetObjTypeFromArr<typeof arr> // 相当于type ArrType = string | number | boolean
其中infer Item
表示让ts自己推断数组里的数据类型是什么,并赋值给类型Item。上面代码总体的意思是:当传递给GetObjTypeFromArr的泛型是数组类型时,取出数组内部的数据类型,否则返回never。
下面再贴出官方例子:
infer推断注意事项
当从函数重载(即多种调用参数)推断类型时,只会从最后一种调用签名推断类型,如
declare function stringOrNum(x: string): number;
declare function stringOrNum(x: number): string;
declare function stringOrNum(x: string | number): string | number;
type T1 = ReturnType<typeof stringOrNum>; // 相当于type T1 = string | number
若将上面第二和第三行代码位置调换,则
type T1 = ReturnType<typeof stringOrNum>; // 相当于type T1 = string
其中ReturnType是定义为
type ReturnType<T extends (...args: any) => any> = T extends (...args:any) => infer Retrun ? Return : any
Condition Type注意事项
- 当条件类型作用于泛型,并且传递的泛型为组合类型时,组合类型中的每种类型都会受条件类型影响,如
type ToArrayType<Type> = Type extends any ? Type[] : never;
表示将传递过来的任意类型用数组类型封装,但Type为组合类型时,比如string | number。string和number会分别受Type extends any ? Type[] : never
影响,如
type ToArrayType<Type> = Type extends any ? Type[] : never;
type StrOrNumberArrType = ToArrayType<string | number> // 相当于type StrOrNumberArrType = string[] | number[]
- 当想让组合类型作为一个整体被影响时,需要用[]包裹住定义的泛型,如
type ToArrayType<Type> = [Type] extends any ? Type[] : never;
type StrOrNumberArrType = ToArrayType<string | number> //type StrOrNumberArrType = (string | number)[]
let sna:StrOrNumberArrType = [1,'1']
注意
type ToArrayType<Type> = Type extends any ? Type[] : never;
type ToArrayType<Type> = [Type] extends any ? Type[] : never;
之间的区别