1. infer
infer 的定义:infer 表示在 extends 条件语句中以占位符出现的用来修饰数据类型的关键字,被修饰的数据类型等到使用时才能被推断出来。
1. infer例子
infer 占位符式的关键字出现的位置:通常infer出现在以下三个位置上。
- infer 出现在 extends 条件语句后的函数类型的参数类型位置上
- infer 出现在 extends 条件语句后的函数类型的返回值类型上
- infer 会出现在类型的泛型具体化类型上。
infer 举例1:
// 1)infer 出现在 extends 条件语句后的函数类型的参数类型位置上
interface Customer {
custname: string
buymoney: number
}
type custFuncType = (cust: Customer) => string
type inferType<T> = T extends (params: infer P) => any ? P : T
type inferResultType = inferType<custFuncType>
infer 举例2:
// 2)infer 出现在 extends 条件语句后的函数类型的返回值类型上
interface Customer {
custname: string
buymoney: number
}
type custFuncType = (cust: Customer) => string // 函数类型
type inferType<T> = T extends (params: any) => infer P ? P : T
type inferResultType = inferType<custFuncType> // 输出函数的返回值类型string
infer 举例3:
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
type ElementOf0<T> = T extends Set<infer E> ? E : never
2. 【 infer 】构建带参数的工厂实例方法
1. 获取构造函数参数
class TestClass {// 准备类
public name: string
public classno: number
constructor(name: string, classno: number) {
this.name = name;
this.classno = classno
}
eat() {
console.log("姓名为: " + this.name);
}
}
type ConstructorParametersType<T extends new (...args: any[]) => any>
= T extends new (...args: infer P) => any ? P : never
let constructorParameters: ConstructorParametersType<typeof TestClass>
2. 获取类实例
class TestClass {// 准备类
public name: string
public classno: number
constructor(name: string, classno: number) {
this.name = name;
this.classno = classno
}
eat() {
console.log("姓名为: " + this.name);
}
}
type InstanceType<T extends new (...args: any[]) => any>
= T extends new (...args: any) => infer P ? P : never
let instanceType: InstanceType<typeof TestClass>
3. 创建带参数的构造函数工厂实例方法
type Constructor<T> = new (...args: any[]) => T
function createInstance<T>(constructor: Constructor<T>, ...args: any[]) {
return new constructor(args[0], args[1])
}
// createInstance<TestClass>(TestClass, "wangwu", 105).eat();
createInstance(TestClass, "wangwu", 105).eat();
2. Exclude(排除)
Exclude<T, U> 的作用是将某个类型中属于另一个的类型移除掉
1. demo
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // "c"
type T2 = Exclude<string | number | (() => void), Function>; // string | number
2. 实现
如果 T 能赋值给 U 类型的话,那么就会返回 never 类型,否则返回 T 类型。最终实现的效果就是将 T 中某些属于 U 的类型移除掉
type MyExclude<T, U> = T extends U ? never : T;
3. Extract(提取)
Extract<T, U> 的作用是将某个类型中属于另一个的类型提取出来
1. demo
type beginType1 = string | number extends string ? string | number : never// never
type extractUnionType = Extract<string | number, string>//string
type extractUnionType2 = Extract<string | number, number>//number
type extractUnionType3 = Extract<string, string | number>//string
type extractUnionType4 = Extract<number, string | number>//number
这里为什么beginType1是never类型而不是string | number类型。
2. 实现
type Extract<T, U> = T extends U ? T : never
3. Extract 在 父类和子类中应用
class People {
public name!: string;
public age!: number
public address!: string
}
class ChinesePeople extends People {
private phone!: string
}
type Extract<T, U> = T extends U ? T : never
// 定律:子类 extends 父类, 永远返回true=> 返回T类型
type extractType = Extract<ChinesePeople, People> // ChinesePeople
// 定律: 父类 extends 子类返回false 因为父类继承子类本身不成立,所以一般都为false
// 但如果希望人为制造一个true 获取到People, 那只有子类实例属性或实例方法个数必须和父类一样多
type extractType2 = Extract<People, ChinesePeople> // never
4.函数的泛型约束
函数类型上的泛型约束,参数类型和返回值完全相同的情况下:
- 参数少的函数类型 extends 参数多的函数类型 返回true
- 参数多的函数类型 extends 参数少的函数类型 返回false
type func1 = (one: number, two: string) => string
type func2 = (one: number) => string
// 函数类型上的泛型约束 参数类型和返回值完全相同的情况下,
// 参数少的函数类型 extends 参数多的函数类型 返回true
// 参数多的函数类型 extends 参数少的函数类型 返回false
type beginType1 = func1 extends func2 ? func1 : never// never
type beginType2 = func2 extends func1 ? func2 : never// never
type extractType1 = Extract<func1, func2>//never
type extractType2 = Extract<func2, func1>//= (one: number) => string
4. Pick
Pick<T, K extends keyof T> 的作用是将某个类型中的子属性挑出来,变成包含这个类型部分属性的子类型。
type MyPick<T, K extends keyof T> = {
[P in K]: T[P];
};
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Pick<Todo, "title" | "completed">;
const todo: TodoPreview = {
title: "Clean room",
completed: false
};
5. Omit
Omit<T, K extends keyof T> 的作用是将某个类型中的子属性排除。
type MyOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Pick<Todo, "description">;
const todo: TodoPreview = {
title: "Clean room",
completed: false
};
6. ReturnType
ReturnType<T> 的作用是用于获取函数 T 的返回类型。
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
type T0 = ReturnType<() => string>; // string\
type T1 = ReturnType<(s: string) => void>; // void\
type T2 = ReturnType<<T>() => T>; // {}\
type T3 = ReturnType<<T extends U, U extends number[]>() => T>; // number[] type T4 = ReturnType<any>; // any\
type T5 = ReturnType<never>; // any\
type T6 = ReturnType<string>; // Error\
type T7 = ReturnType<Function>; // Error
7. InstanceType
InstanceType<T>用来获取类的实例类型
InstanceType: zhuanlan.zhihu.com/p/52662645
type InstanceType<T extends new (...args: any[]) => any>
= T extends new (...args: any) => infer P ? P : 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);
}
}
let instanceType: InstanceType<typeof TestClass>
8. Recode
Record<K extends keyof any, T> 的作用是将 K 中所有的属性的值转化为 T 类型
type Record<K extends keyof any, T> = { [P in K]: T; };
1. 扁平化数据
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
}
]
type Record<K extends keyof any, T> = {
[P in K]: T
}
let goodRecord: Record<number, Goods> = {}
goodsList.forEach((goods) => {
goodRecord[goods[goodSymid]] = goods;
})
// //goodRecord: {
// '101': { name: '苹果', price: 9, [Symbol(goodid)]: 101 },
// '102': { name: '香蕉', price: 3, [Symbol(goodid)]: 102 },
// '103': { name: '橘子', price: 3, [Symbol(goodid)]: 103 }
// }
for (let goodid in goodRecord) {
console.log(goodid, ":", goodRecord[goodid])
}
const goodSymid = Symbol("goodid")
interface Goods {
[goodSymid]: number
name: string
price: number
}
type Record<T> = {
[P in keyof any]: T
}
type resultGoodsType = Record<Goods>
let goodRecord: Record<Goods> = {}
let good: Goods = { [goodSymid]: 101, "name": "苹果", "price": 9 }
goodRecord[103] = good;
goodRecord["香蕉"] = good
goodRecord[good[goodSymid]] = good
console.log("goodRecord:", goodRecord);
// Record类型对于取出来的对象,可以自动提示输出对象的属性和方法
for (let goodid in goodRecord) {
let good = goodRecord[goodid];
console.log(goodid, ":", good)
}
2. Recode与Map区别
实际开发为什么我们在显示数据,数据扁平化时用Record
原因1:是因为Record有多种实现方式,比如S100实现方式,Map就需要改底层源码才能做到【一般是不会改的】
原因2:Record是属于一个轻量级的type类型,Map相对Record是重量级,而且Map需要new出来的,所以要更加占用内存空间
- 如果读取数据和显示数据频繁,就应该采用Record
- 如果增删改比较多,那还是使用Map
9. Required
Required<T>的作用就是将某个类型里的属性全部变为必选
type Required<T> = {
[P in keyof T]-?: T[P];
}
原来在 Required<T> 工具类型内部,通过 -? 移除了可选属性中的 ? ,使得属性从可选变为必选的
10. Partial
Partial<T> 的作用就是将某个类型里的属性全部变为可选项 ?
type Partial<T> = {
[P in keyof T]?: T[P];
};
在以上代码中,首先通过 keyof T 拿到 T 的所有属性名,然后使用 in 进行遍历,将值赋给 P ,最 后通过 T[P] 取得相应的属性值。中间的 ? 号,用于将所有属性变为可选。
11. ReadOnly
ReadOnly 一次性全部变成可读选项
type ReadOnly<T> = {
readonly [P in keyof T]: T[P]
}
12. 特殊符号
1. ! 非空断言操作符
1. 忽略undefined和null
2. 调用函数忽略undefined
3. 确定赋值断言
2. ?. 可选链
3. ?? 空值合并运算符
当左侧操作数为 null 或 undefined 时,其返回右侧的操作数,否则返回左侧的操作数。