infer
-
infer 的定义:infer 表示在 extends 条件语句中以占位符出现的用来修饰数据类型的关键字,被修饰的数据类型等到使用时才能被推断出来。
-
infer 占位符式的关键字出现的位置:通常infer出现在以下三个位置上。
- infer 出现在 extends 条件语句后的函数类型的参数类型位置上
- infer 出现在 extends 条件语句后的函数类型的返回值类型上
- infer 会出现在类型的泛型具体化类型上。
-
infer 举例1:
type inferType<T> = T extends (param: infer P) => any ? P : T
interface Customer {
custname: string
buymoney: number
}
type custFuncType = (cust: Customer) => void
type inferType = inferType<custFuncType>// 获取到函数参数的类型 Customer
const cust: inferType = { custname: "wangwdu", buymoney: 23 }
- infer 举例2:
class Subject {
constructor(public subid: number, public subname: string) {
}
}
let chineseSubject = new Subject(100, "语文")
let mathSubject = new Subject(101, "数学")
let englishSubject = new Subject(101, "英语")
let setZhangSanSubject = new Set([chineseSubject, mathSubject]);
type ss = typeof setZhangSanSubject
// 获取 Set 的指定泛型的类型
type ElementOf0<T> = T extends Set<infer E> ? E : never
- 构建带参数的工厂实例方法
class TestClass {// 准备类
public name: string
public classno: number
constructor(name: string, classno: number) {
this.name = name;
this.classno = classno
}
eat() {
console.log("姓名为: " + this.name + "班级:" + this.classno);
}
}
// 获取构造函数 T 的参数类型 P,P的类型会在执行时候确定
type ConstructorParametersType<T extends new (...args: any[]) => any>
= T extends new (...args: infer P) => any ? P : never
// 通用的构造器类型,T 是构造函数返回对象的类型
type Constructor<T> = new (...args: any[]) => T
// 根据构造函数类型,以及传入的参数,创建一个实例对象
function createInstance<T, C extends new (...args: any[]) => any>(constructor: Constructor<T>,
...args: ConstructorParametersType<C>) {
return new constructor(args[0], args[1])
}
type classType = typeof TestClass
createInstance<TestClass, classType>(TestClass, "wangwu", 105).eat();
createInstance(TestClass, ["wangwu", 23])
- 联合 Vue3 源码 理解 infer
// 类似于 infer 举例2的用法
function unref<T>(ref: T): T extends Ref<infer V> ? V : T {
return isRef(ref) ? (ref.value as any) : ref
}
Extract 和 Exclude
- 详解 Extract.ts
// Extract 类型定义格式
type Extract<T, U> = T extends U ? T : never
class People {
public name!: string;
public age!: number
public address!: string
eat() {
}
}
class ChinesePeople extends People {
private phone!: string
}
let cp = new ChinesePeople();
// Extract 是TS提供的一个TS高级type类型【简称TS高级类型】
type Extract<T, U> = T extends U ? T : never
// Extract 在 父类和子类中应用
// 定律:子类 extends 父类=>子类 extends 父类永远返回true=>返回T类型
type extractType = Extract<ChinesePeople, People> // ChinesePeople
// 定律: 父类 extends 子类=>父类 extends 子类返回false 因为父类继承子类本身不成立,所以一般都为false
// 但如果希望人为制造一个true 获取到People
// 那只有子类实例属性或实例方法个数必须和父类一样多
type extractType2 = Extract<People, ChinesePeople> // never
- 从结果上详细对比的 Extract 泛型约束和类型断言【父子类】
// 类型断言 在父类和子类如何断言
let people: People = new People();
let ChinesePeople2 = people as ChinesePeople// 父类对象变量断言成子类类型 成立
let americanPeople: ChinesePeople = new ChinesePeople();
let p: People = americanPeople as People;// 子类对象变量断言成父类类型 成立
- 从结果上详细对比 Extract 泛型约束和类型断言【联合类型】
type Extract<T, U> = T extends U ? T : never
// TS
type beginType1 = string | number extends string ? string | number : never// never
type extractUnionType = Extract<string | number, string>//string || never
type extractUnionType2 = Extract<string | number, number>//number
type beginType3 = string extends string | number ? string : never// string
type beginType4 = number extends string | number ? number : never// number
type extractUnionType3 = Extract<string, string | number>//string
type extractUnionType4 = Extract<number, string | number>//number
- 从结果上详细对比 Extract 泛型约束和类型断言 【函数】
// 函数的泛型约束
// 函数类型上的泛型约束 参数类型和返回值完全相同的情况下,
// 参数少的函数类型 extends 参数多的函数类型 返回true
// 参数多的函数类型 extends 参数少的函数类型 返回false
type beginType1 = func1 extends func2 ? func1 : never// never
type beginType2 = func2 extends func1 ? func2 : never// (one: number) => string
type extractType1 = Extract<func1, func2>//never
type extractType2 = Extract<func2, func1>//= (one: number) => string
- Extract 真实应用场景.ts
type Extract<T, U> = T extends U ? T : never
type CrosTyp<T> = Extract<T, object>
// 简化写法,不用一直 泛型 extends object 进行泛型约束了
function cross<T, U, V>(objOne: CrosTyp<T>, objTwo: CrosTyp<U>, objThree?: CrosTyp<V>)
- Exclude
// 写法
type Exclude<T, U> = T extends U ? never : T
interface Worker {
name: string
age: number
email: string
salary: number
}
interface Student {
name: string
age: number
email: string
grade: number
}
// 用Extract来完成的获取Worker接口类型中的"age" | "email" | "salary"三个属性组成的联合类型
type Extract<T, U> = T extends U ? T : never
type isResultType = Extract<"age" | "email" | "salary" | "xx", keyof Worker>
//排除条件成立的类型,保留不符合泛型约束条件的类型
type Exclude<T, U> = T extends U ? never : T
// 用Exclude来完成的获取Worker接口类型中的"age" | "email" | "salary"三个属性组成的联合类型
type isResultType2 = Exclude<"age" | "email" | "salary" | "xx", keyof Worker>//xx
type isResultType22 = Exclude<"name" | "xx", keyof Worker>//xx
type isResultType23 = Exclude<"name", keyof Worker>//never
type isResultType24 = Exclude<"name" | "age" | "email" | "salary", "name">// "age" | "email" | "salary"
// 获取Woker接口类型中存在的属性,但是在学生接口类型中不存在的属性
type isResultType25 = Exclude<keyof Worker, keyof Student>//salary
Record
K extends keyof any等价于K extends keyof string |number |symbolK in keyof any等价于[x: string]:,可以接number 或 symbol,自动转成字符串。如果写成这种形式,就不能接收数组了。- 写法
type Customer = {
custname: string,
age: number
phone: string
}
type Record<K extends keyof any, T> = {
[P in K]: T
}
type resultRecord = Record<string, Customer>//S100
let obj: resultRecord =
{
"usernamed": { custname: "wangwu", age: 23, phone: "111" },
"agde": { custname: "lisi", age: 33, phone: "23" }
}
// 如果传入number类型,就可以接数组
type resultRecord2 = Record<number, Customer>//S100
let objarray: resultRecord2 = [{ custname: "wangwu", age: 23, phone: "111" },
{ custname: "lisi", age: 33, phone: "23" }]
- Record 完成数据扁平化
type Record<K extends keyof any, T> = {
[P in K]: T
}
let goodRecord: Record<number, Goods> = {}
const goodSymid = Symbol("goodid")
interface Goods {
[goodSymid]: number
name: string
price: number
}
const goodsList: Goods[] = [
{
[goodSymid]: 101,
"name": "苹果",
"price": 9
},
{
[goodSymid]: 102,
"name": "香蕉",
"price": 3
},
{
[goodSymid]: 103,
"name": "香蕉",
"price": 3
}
]
goodsList.map((goods) => {
goodRecord[goods[goodSymid]] = goods;
})
// 泛型自动提示
console.log("goodRecord:", goodRecord);
for (let key in goodRecord) {
console.log(goodRecord[key].name)
}
// type Record2<T> = {
// [x: string]: T,// 字符串索引可以是数字类型,可以是字符串类型,最终都会转换为字符串类型
// //[x: number]: T,// 字符串索引可以是数字类型 [x: number]可以最终合成一个数组的索引
// //[x:symbol]:T//索引签名参数类型必须为 "string" 或 "number"
// }
- object 和 Record
- 区别1:Record 获取到是索引参数类型,所以可以赋初值为{} 。object也可以,但是再次赋值,比如: goodRecord[103] = good2;会出现错误,会查找103属性是否存在于object类型的对象变量中
- 区别2:Record是泛型,获取值可以有自动提示功能,而object无法实现自动提示。
- object 相比 Map 的好处
- Record有多种实现方式,比如S100实现方式,Map就需要改底层源码才能做到【一般是不会改的】
// type Record<T> = {// S100 [P in keyof any]: T }- Record是属于一个轻量级的type类型, Map相对Record是重量级,而且Map需要new出来的,所以要更加占用内存空间。如果读取数据和显示数据频繁,就应该采用Record,如果增删改比较多,那还是使用Map。
Pick
- Pick 主要用于提取某种数据类型的属性,但实际工作中,主要用来提取接口或 type 定义的对象类型中的属性
- Pick+ Record 结合应用
// 理解 Pick
// 而 keyof用来获取接口的属性名【key】组成的联合类型
// K 如果 属于 keyof T 联合类型或者它的子类型
// 那么 K extends keyof T就成立
type Pick<T, K extends keyof T> = {
// in是类型映射,=for...in 循环迭代所有的K的类型
[P in K]: T[P]
}
const todonew: Pick<Todo, "title"> = {
"title": "下午3点美乐公园参加party"
}
const todonew2: Pick<Todo, "title" | "completed"> = {
"title": "下午3点美乐公园参加party",
"completed": false
}
interface Todo {
title: string
completed: boolean
description: string
}
type TodoPreview = Pick<Todo, "title" | "completed">
const todo: TodoPreview = {
title: 'Clean room',
completed: false
}
const todo2: Pick<Todo, "title" | "completed"> = {
title: 'Clean room',
completed: false
}
export { }
Partial、Required、ReadOnly
// Partial 一次性全部变成可选选项的type高级类型
type Partial<T> = {
[P in keyof T]?: T[P]
}
interface ButtonProps {
type: 'button' | 'submit' | 'reset'
text: string
disabled: boolean
onClick: () => void
}
let props: Partial<ButtonProps> = {
text: "登录"
}
// Required 和Partial相反 一次性全部变成必选选项的type高级类型
type Required<T> = {
[P in keyof T]-?: T[P]
}
// ReadOnly 一次性全部变成可读选项的type高级类型
type ReadOnly<T> = {
readonly [P in keyof T]: T[P]
}
Omit 反向抓取属性数据
和 Pick 正好相反
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
interface Todo {
title: string
completed: boolean
description: string
// phone: number
}
type TodoPreview = Omit<Todo, "description">//type TodoPreview={}
const todo: TodoPreview = {
title: 'Clean room',
completed: false,
}
export { }