基于条件类型
前置
在了解基于条件类型的内置类型前,需要了解条件类型的相关知识
条件类型
对于条件类型,官方的定义是
T extends U ? X : Y
上面的类型意思是,若T能够赋值给U,那么类型是X,否则为Y。
从上面的描述可以看出,条件类型看起来有点像 JavaScript 中的三元表达式(T extends U ?真 X : 假 Y )
也就是判断extends左边是否是右边的子类型,如果子类型 extends 父类型 = true,否则false
子类型: never -> 字面量类型 -> 基础类型 -> 包装类型 -> any / unknown
type T1 = never extends "str" ? true : false; // true
type T2 = "str" extends string ? true : false; // true
type T3 = string extends String ? true : false; // true
限制:条件类型只能在type中使用
分发机制
条件类型有分发机制,产生分发机制的条件
1.联合类型通过泛型传递
2.类型需要是裸类型 (泛型没有和别人搭配)
简单的例子如下:
type Temp<T> = T extends string ? 1 : 2;
type Temp = Temp<string | number>; // 1 | 2
1.联合类型
string | number通过泛型传递2.extends左边泛型T是裸类型(如果左边是
T & {}或者[T]等就不是裸类型)
推导过程:
1.满足分发机制的条件
2.把extends左边的T也就是联合类型string | number的每个类型和extends右边的类型进行比较,相当于
string extends string ? 1 : 2; // 1
number extends string ? 1 : 2; // 2
3.分发比较的结果会组成联合类型1|2返回
Extract
提取T中可以赋值给U的类型
// Extract<T, U>
type T = Extract<1 | 2 | 3 | 4, 1 | 2 | 5>; // 1 | 2
官方实现
type Extract<T, U> = T extends U ? T : never;
推导过程:
1、联合类型1 | 2 | 3 | 4通过泛型传递,extends左边泛型T是裸类型,满足分发机制
2、把extends左边的T也就是联合类型`1 | 2 | 3 | 4的每个类型和extends右边的类型1 | 2 | 5进行比较,相当于
1 extends 1 | 2 | 5 ? 1 : never; // 1
2 extends 1 | 2 | 5 ? 2 : never; // 2
3 extends 1 | 2 | 5 ? 3 : never; // never
4 extends 1 | 2 | 5 ? 4 : never; // never
3、分发比较的结果会组成联合类型1|2返回
Exclude
从T中剔除可以赋值给U的类型
// Exclude<T, U>
type T = Exclude<1 | 2 | 3 | 4, 1 | 2>; // 3 | 4
官方实现
type Exclude<T, U> = T extends U ? never : T;
这里的推导过程与
Extract类似,只是类型T能赋给U时,返回的是never,不能赋给U返回的是T,达到剔除U的效果
NonNullable
从T中剔除null和undefined
// NonNullable<T>
type T = NonNullable<string | number | undefined>; // string | number
官方实现
// 旧版的实现
type NonNullable<T> = T extends null | undefined ? never : T;
// 新版的实现
type NonNullable<T> = T & {};
旧版的实现利用的是条件类型的分发机制,类似Exclude的效果将null和undefined排除
新版的实现利用的是&交叉类型
- 类型
T与空对象类型{}相交,结果只包含T类型的属性和方法 - 交叉类型不允许存在
null和undefined类型
所以排除null和undefined
基于映射类型
前置
在了解基于映射类型的内置类型前,需要了解相关知识
keyof
keyof关键字可以获取对象中的类型作为联合类型
type T1 = {
name: string;
age: number;
}
type T2 = keyof T1 // 'name' | 'age'
映射类型
映射类型的语法
{ [ K in T ] : U }
1、映射类型类似for...in,T是一个联合类型,然后对联合类型进行遍历;K是则是联合类型T的每一项,U是每一项对应的类型
2、映射类型相当于创建一个新的对象类型,左边的[ K in T ]作为对象类型的所有属性名,右边的U是对应属性名的属性值
3、映射类型只能在type中使用
type T1 = 'a' | 'b';
type T2 = {
[ K in T1 ] : string;
}
// type T2 = {
// a: string;
// b: string;
// }
映射类型常与keyof一起配合使用,将一个对象类型变成一个新对象类型
type T1 = {
name: string;
age: number;
}
type T2 = {
[ K in keyof T1 ] : any;
}
// type T2 = {
// name: any;
// age: any;
// }
Partial
指定类型T的所有属性都变为可选
// Partial<T>
type T1 = {
name: string;
age: number;
}
type T2 = Partial<T1>
// type T2 = {
// name?: string
// age?: number
//}
官方实现
type Partial<T> = {
[key in keyof T]?: T[key];
};
利用映射类型,遍历类型,在属性名后面加?变成可选属性
Required
指定类型T的所有属性都变为必选
// Required<T>
type T1 = {
name?: string;
age?: number;
}
type T2 = Required<T1>
// type T2 = {
// name: string
// age: number
//}
官方实现
type Required<T> = {
[key in keyof T]-?: T[key];
};
遍历对象类型,在属性名后面加-?变成必选属性
Readonly
指定类型 T 的所以属性都变成只读
// Readonly<T>
type T1 = {
name: string;
age?: number;
}
type T2 = Readonly<T1>
// type T2 = {
// readonly name: string
// readonly age: number
//}
官方实现
type Readonly<T> = {
+readonly [K in keyof T]: T[K];
};
遍历对象类型,在属性名前面加readonly变成只读属性
Pick
从类型 T中选择指定的属性 K,返回一个新的类型,只包含选定的属性
// Pick<T, K extends keyof T>
type T1 = {
name: string
age: number
}
type T2 = Pick<T1, 'age'>
// type T2 = { age: number }
官方实现
type Pick<T, K extends keyof T> = {
[key in K]: T[key];
};
这里的K extends keyof T中的extends是泛型约束,表示泛型K只能传递与keyof T相关的字面量类型。
推导过程:
1、遍历选定的联合类型K,K中的任意一项key作为新类型的属性名
2、从类型T中找到属性名为key对应的属性值,赋给对应的属性名,组成一个新类型
Omit
从类型T 中排除指定的属性 K,返回一个新的类型,不包含指定的属性
// Omit<T, K extends keyof T>
type T1 = {
name: string
age: number
}
type T2 = Omit<T1, 'age'>
// type T2 = { name: string }
官方实现
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
Omit利用了之前学习的Pick和Exclude工具类型,将类型T需要排除的属性K改为获取类型T指定的属性,这个属性是类型T排除了K中的属性之后的属性
Record
// Record<T, R>
type T = Record<string, any>
const obj: T = {
name: string
age: number
}
// 使用 Record 创建了一个名为 data 的对象,它的属性名是字符串类型,属性值是 Person 类型的对象。
// 这样我们就可以通过属性名来访问对应的 Person 对象。
type Person = {
name: string;
age: number;
};
const data: Record<string, Person> = {
john: { name: "John", age: 25 },
jane: { name: "Jane", age: 30 },
};
官方实现
type Record<T extends keyof any, K> = {
[key in T]: K;
};
接受两个类型参数:T和K。T是一个联合类型,表示要创建的对象的属性名称的集合,而 K则表示属性的类型。不会限制对象的属性数量,它只是指定了属性的名称和类型。
基于推断类型
前置
inter
infer只能在条件类型中使用,提取待推断的类型变量。infer放在不同的位置,可以帮我们区不同位置的类型
inter示例如下:
// 获取函数类型的返回值
type Func = (a: string, b: number) => { name:string; age:number; }
type ReturnType<T> = T extends (...args: any) => infer R ? R : any;
type T1 = ReturnType<Func>
// type T1 = {
// name: string;
// age: number;
// }
推导过程:
1、待推断的类型infer R的位置放在(...args: any) => infer R函数的返回值的地方,所以这里提取的就是函数类型的返回值
2、最后ReturnType的结果返回的R就是推导出来的函数类型的返回值
另一个示例如下:
// 获取参数类型
type Func = (a: string, b: number) => { name:string; age:number; }
type Parameters<T> = T extends (
...args: infer R
) => any
? R
: false;
type T1 = Parameters<Func>
// type T1 = [a: string, b: number]
推导过程:
1、infer R的位置放在...args类型的地方,所以这里提取的就是参数的类型
ReturnType
获取函数返回值类型
官方实现
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
在上面inter获取函数返回值的例子里加上泛型约束
Parameters
返回函数函数参数类型元组
官方实现
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
在上面inter获取函数参数的例子里加上泛型约束