likes
comments
collection
share

Typescript 高级类型-映射类型

作者站长头像
站长
· 阅读数 11

一、映射类型

  • 映射类型是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类型推断。inferInstanceType的构造方法返回结果处使用。目的推断T的实例类型。有兴趣的朋友可以试着分析Parameters<T>

4、in

用于遍历目标类型的公开属性名;类似for in操作。

// 例如:Record
type Record<K extends keyof any, T> = {
    [P in K]: T;
};

通过之前对extendskeyof的了解,再加上extends再泛型中使用。可以得出extends用于约束类型Kin是用于遍历目标类型;所以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的用法是变量和类型操作

萌新,如有错误欢迎指正 (ง •_•)ง ;一起学习,一起进步