Typescript 高级类型-映射类型
一、映射类型
- 映射类型是TS允许将一个类型映射成另外一个类型。
详细内置高级类型,可查看官方lib.es5.d.ts。注意类型操作都是操作的对象是类型
先定义两个简单类型:
// 响应数据
interface Res {
code: number;
msg: string;
data: any;
errMsg?: string;
}
type ResCode = 200 | 300 | 400 | 500;
1、可选属性
语法:
type partial = Partial<T>
使用:
type ResPartial = Partial<Res>
// 等价
type ResPartial = {
code?: number;
msg?: string;
data?: any;
errMsg?: string;
}
原理:
type Partial<T> = {
[P in keyof T]?: T[P];
};
关键操作 in"、"keyof T"、"?"
2、必选属性
语法:
type required = Required<T>
使用:
type ResRequired = Required<T>
// 等价
type ResRequired = {
code: number;
msg: string;
data: any;
errMsg: string;
}
原理:
type Required<T> = {
[P in keyof T]-?: T[P];
};
关键操作 "in"、"keyof T"、"-?"
3、只读属性
语法:
type readonly = Readonly<T>;
使用:
type ResReadonly = Readonly<res>
// 等价
type ResReadonly = {
readonly code: number;
readonly msg: string;
readonly data: any;
readonly errMsg?: string;
}
原理:
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
关键操作 "in"、"keyof T"
4、选择/挑选属性
语法:
type pick = Pick<T, k in keyof T>
使用:
type ResPick = Pick<Res, "code" | "data">
// 等价
type ResPick = {
code: number;
data: any;
}
原理:
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
关键操作 "extends"、"keyof T"、"in"
5、动态构造
语法:
type record = Record<K extends keyof any, T>
使用:
type recordString = Record<string, any>
// 等价
type recordString = {
[x: string]: any;
}
type recordReq = Record<"id" | "token" | "data", any>
// 等价
type recordReq = {
id: any;
token: any;
data: any;
}
原理:
type Record<K extends keyof any, T> = {
[P in K]: T;
};
关键操作 "extends"、"keyof any"、"in"
6、排除属性
语法:
type exclude = Exclude<T, U>
使用:
type excludeResCode = Exclude<ResCode, 200>
// 等价
type excludeResCode = 300 | 400 | 500
原理:
type Exclude<T, U> = T extends U ? never : T;
关键操作 "extends"、"?"、"never"
注意:和Pick的用法差别,Extract操作的是联合类型,Pick可以操作对象类型、联合类型
7、提取属性
语法:
type extract = Extract<T, U>
使用:
type extractResCode = Extract<ResCode, 200 | 300>;
// 等价
type extractResCode = 200 | 300;
原理:
type Extract<T, U> = T extends U ? T : never;
关键操作 "extends"、"?"、"never"
注意: 和Exclude的原理区别
8、忽略属性
语法:
type omit = Omit<T, K>
使用:
type omitRes = Omit<Res, 'code' | 'msg'>
// 等价
type omitRes = {
data: any;
errMsg?: string;
}
原理:
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
关键操作 "extends"、"keyof any"、"Exclude"
9、排除null和undefined
语法:
type nonNullable = NonNullable<T>
使用:
type Msg = string | null | number | undefined;
type MsgNonNullable = NonNullable<reMsgsMsg>;
// 等价
type nonNullable = string | number;
原理:
type NonNullable<T> = T extends null | undefined ? never : T;
关键操作 "extends"、"?"、"never"
10、获取函数参数返回类型
语法:
type parameters = Parameters<T>;
使用:
interface ShowInfo {
(msg: string, type: number): string;
}
type parameters = Parameters<ShowInfo>;
// 结果
type parameters = [msg: string, type: number]
原理:
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
关键操作 "extends"、"?"、"infer"
11、获取函数返回类型
语法:
type returnType = ReturnType<T>;
使用:
interface ShowInfo {
(msg: string, type: number): string;
}
type returnType = ReturnType<ShowInfo>;
// 结果
type returnType = string
原理:
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
关键操作 "extends"、"?"、"infer"
二、关键字/操作符
在一、映射类型
列举了常用的内置类型映射。当然如果你注意到关键操作,你会发现其实原理都是使用"keyof"、"extends"、"infer"、"in"等类型关键字/操作符扩展而成。
1、keyof
获取类型T的所有key的联合类型;可以操作接口、类以及基本数据类型
type ResKeyof = keyof Res;
// 等价
type ResKeyof = "code" | "msg" | "data" | "errMsg";
// 类
class Animal {
constructor() {}
eat() {}
walk() {}
}
type AnimalType = keyof Animal; // "eat" | "walk"
// 例如:获取对象属性、数组索引值
function getProto<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
2、extends
用于类型的继承或者条件判断,当与三目运算符(?:
)使用的时候是条件判断;其他情况都是继承。
// 继承
interface Person {
name: string;
age: number;
}
interface Student extends Person {
grade: number | string;
}
// 等价
interface Student {
name: string;
age: number;
grade: number | string;
}
// 判断,就比如内置的NonNullable<T>;
type NonNullable<T> = T extends null | undefined ? never : T;
其实当extends
与泛型使用可以类型约束
,例如:
// keyof any => string | number | symbol
function showInfo<T extends keyof any>(info: T) {
return info;
}
其本质也可以理解为继承
,我们可以keyof any
返回的是一个联合类型string | number | symbol
;T通过extends继承了keyof any
。也就是说info
的类型就是string | number | symbol
联合类型。所以当传入一个非string | number | symbol
的类型时候就会报错。
3、infer
条件类型
中的类型推断。目前TS类型条件语句不正是上一条extends
的用法吗? 目前类型条件判断仅当extends
与三目运算符(?:
)一起使用的时候。所以infer
的使用离不开extends
和三目运算符(?:
)。
例如:内置类型InstanceType
type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;
通过分析第一个extends
与泛型使用,并且没有和三目运算符一起使用所以是类型约束
;约束类型T是一个类。第二个extends
与三目运算形成条件判断,所以可以使用infer
类型推断。infer
在InstanceType
的构造方法返回结果处使用。目的推断T的实例类型。有兴趣的朋友可以试着分析Parameters<T>
4、in
用于遍历目标类型的公开属性名;类似for in
操作。
// 例如:Record
type Record<K extends keyof any, T> = {
[P in K]: T;
};
通过之前对extends
和keyof
的了解,再加上extends
再泛型中使用。可以得出extends
用于约束类型K
。in
是用于遍历目标类型;所以P
是类型K
中的属性名。再结合类型约束P
只能是string、number、symbol
三中的一种。
同样in
也可以遍历枚举。
enum Color {
Red,
Green,
Blue,
}
type ColorType = {
[P in Color]: string;
};
5、is
用于判断变量
是否属于某个类型
。可以理解为 "!!"操作。
// 语法: {变量} is {类型}
function isNull(val: unknown): val is null {
return val === null;
}
注意:is的用法是变量和类型操作
萌新,如有错误欢迎指正 (ง •_•)ง ;一起学习,一起进步
转载自:https://juejin.cn/post/6962441571018997791