共计 4651 个字符,预计需要花费 12 分钟才能阅读完成。
本文会和大家具体介绍 TypeScript 中的映射类型(Mapped Type),看完本文你将学到以下知识点:
- 数学中的映射和 TS 中的映射类型的关系;
- TS 中映射类型的利用;
- TS 中映射类型修饰符的利用;
接下来会先从「数学中的映射」开始介绍。
本文应用到的 TypeScript 版本为 v4.6.2。
如果你对 TypeScript 还不相熟,能够看上面几篇材料:
- 一份不可多得的 TS 学习指南(1.8W 字)
- 了不起的 TypeScript 入门教程
一、什么是映射?
在学习 TypeScript 类型零碎时,尽量多和数学中的汇合类比学习,比方 TypeScript 中的联结类型,相似数学中的并集等。
在数学中,映射是指 两个元素的汇合之间元素互相对应的关系,比方下图:
(起源:https://baike.baidu.com/item/%E6%98%A0%E5%B0%84/20402621)
能够将映射了解为函数,如上图,当咱们须要将汇合 A 的元素转换为汇合 B 的元素,能够通过 f
函数做映射,比方将汇合 A 的元素 1
对应到汇合 B 中的元素 2
。
这样就能很好的实现映射过程的 复用。
二、TypeScript 中的映射类型是什么?
1. 概念介绍
TypeScript 中的映射类型和数学中的映射相似,可能将一个汇合的元素转换为新汇合的元素,只是 TypeScript 映射类型是将一个类型映射成另一个类型。
在咱们理论开发中,常常会须要一个类型的所有属性转换为可选类型,这时候你能够间接应用 TypeScript 中的 Partial
工具类型:
type User = {
name: string;
location: string;
age: number;
}
type User2 = Partial<User>;
/*
User2 的类型:type User2 = {
name?: string | undefined;
location?: string | undefined;
age?: number | undefined;
}
*/
这样咱们就实现了将 User
类型映射成 User2
类型,并且将 User
类型中的所有属性转为可选类型。
2. 实现办法
TypeScript 映射类型的语法如下:
type TypeName<Type> = {[Property in keyof Type]: boolean;
};
咱们既然能够通过 Partial
工具类型非常简单的实现 将指定类型的所有属性转换为可选类型,那其内容原理又是如何?
咱们能够在编辑器中,将鼠标悬停在 Partial
名称下面,能够看到编辑器提醒如下:
拆解一下其中每个局部:
type Partial<T>
:定义一个类型别名Partial
和泛型T
;keyof T
:通过keyof
操作符获取泛型T
中所有key
,返回一个联结类型(如果不分明什么是联结类型,能够了解为一个数组);
type User = {
name: string;
location: string;
age: number;
}
type KeyOfUser = keyof User; // "name" | "location" | "age"
in
:相似 JS 中for...in
中的in
,用来遍历指标类型的公开属性名;T[P]
:是个索引拜访类型(也称查找类型),获取泛型T
中P
类型,相似 JS 中的拜访对象的形式;?:
将类型值设置为可选类型;{[P in keyof T] ?: T[P] | undefined}
:遍历keyof T
返回的联结类型,并定义用P
变量接管,其每次遍历返回的值为可选类型的T[P]
。
这样就实现了 Partial
工具类型,这种操作方法十分重要,是前面进行 TypeScript 类型体操的重要根底。
对于类型体操的练习,有趣味能够看看这篇文章:
《这 30 道 TS 练习题,你能答对几道?》https://juejin.cn/post/7009046640308781063
三、映射类型的利用
TypeScript 映射类型常常用来复用一些对类型的操作过程,比方 TypeScript 目前反对的 21 种工具类型,将咱们罕用的一些类型操作定义成这些工具类型,不便开发者复用这些类型。
所有已反对的工具类型能够看下官网文档:
https://www.typescriptlang.org/docs/handbook/utility-types.html
上面咱们挑几个罕用的工具类型,看下其实现过程中是如何应用映射类型的。
在学习 TypeScript 过程中,举荐多在官网的 Playground 练习和学习:
https://www.typescriptlang.org/zh/play
1. Required 必选属性
用来 将类型的所有属性设置为必选属性。
实现如下:
type Required<T> = {[P in keyof T]-?: T[P];
};
应用形式:
type User = {
name?: string;
location?: string;
age?: number;
}
type User2 = Required<User>;
/*
type User2 = {
name: string;
location: string;
age: number;
}
*/
const user: User2 = {
name: 'pingan8787',
age: 18
}
/*
报错:Property 'location' is missing in type '{name: string; age: number;}'
but required in type 'Required<User>'.
*/
这边的 -?
符号能够临时了解为“将可选属性转换为必选属性”,下一节会具体介绍这些符号。
2. Readonly 只读属性
用来 将所有属性的类型设置为只读类型,即不能重新分配类型。
实现如下:
type Readonly<T> = {readonly [P in keyof T]: T[P];
}
应用形式:
type User = {
name?: string;
location?: string;
age?: number;
}
type User2 = Readonly<User>;
/*
type User2 = {
readonly name?: string | undefined;
readonly location?: string | undefined;
readonly age?: number | undefined;
}
*/
const user: User2 = {
name: 'pingan8787',
age: 18
}
user.age = 20;
/*
报错:Cannot assign to 'age' because it is a read-only property.
*/
3. Pick 抉择指定属性
用来 从指定类型中抉择指定属性并返回。
实现如下:
type Pick<T, K extends keyof T> = {[P in K]: T[P];
}
应用如下:
type User = {
name?: string;
location?: string;
age?: number;
}
type User2 = Pick<User, 'name' | 'age'>;
/*
type User2 = {
name?: string | undefined;
age?: number | undefined;
}
*/
const user1: User2 = {
name: 'pingan8787',
age: 18
}
const user2: User2 = {
name: 'pingan8787',
location: 'xiamen', // 报错
age: 18
}
/*
报错
Type '{name: string; location: string; age: number;}' is not assignable to type 'User2'.
Object literal may only specify known properties, and 'location' does not exist in type 'User2'.
*/
4. Omit 疏忽指定属性
作用相似与 Pick
工具类型相同,能够 从指定类型中疏忽指定的属性 并返回。
实现如下:
type Omit<T, K extends string | number | symbol> = {[P in Exclude<keyof T, K>]: T[P];
}
应用形式:
type User = {
name?: string;
location?: string;
age?: number;
}
type User2 = Omit<User, 'name' | 'age'>;
/*
type User2 = {location?: string | undefined;}
*/
const user1: User2 = {location: 'xiamen',}
const user2: User2 = {
name: 'pingan8787', // 报错
location: 'xiamen'
}
/*
报错:Type '{name: string; location: string;}' is not assignable to type 'User2'.
Object literal may only specify known properties, and 'name' does not exist in type 'User2'.
*/
5. Exclude 从联结类型中排除指定类型
用来 从指定的联结类型中排除指定类型。
实现如下:
type Exclude<T, U> = T extends U ? never : T;
应用形式:
type User = {
name?: string;
location?: string;
age?: number;
}
type User2 = Exclude<keyof User, 'name'>;
/*
type User2 = "location" | "age"
*/
const user1: User2 = 'age';
const user2: User2 = 'location';
const user3: User2 = 'name'; // 报错
/*
报错:Type '"name"' is not assignable to type 'User2'.
*/
四、映射修饰符的利用
在自定义映射类型的时候,咱们能够应用两个映射类型的修饰符来实现咱们的需要:
readonly
修饰符:将指定属性设置为 只读类型;?
修饰符:将指定属性设置为 可选类型;
后面介绍 Readonly
和 Partial
工具类型的时候曾经应用到:
type Readonly<T> = {readonly [P in keyof T]: T[P];
}
type Partial<T> = {[P in keyof T]?: T[P] | undefined;
}
当然,也能够对修饰符进行操作:
+
增加修饰符(默认应用);-
删除修饰符;
比方:
type Required<T> = {[P in keyof T]-?: T[P]; // 通过 - 删除 ? 修饰符
};
也能够放在后面应用:
type NoReadonly<T> = {-readonly [P in keyof T]: T[P]; // 通过 - 删除 readonly 修饰符
}
五、总结
本文从数学中的映射作为切入点,具体介绍 TypeScript 映射类型(Mapped Type)并介绍映射类型的利用和修饰符的利用。
在学习 TypeScript 类型零碎时,尽量多和数学中的汇合类比学习,比方 TypeScript 中的联结类型,相似数学中的并集等。
学好映射类型,是接下来做类型体操中十分重要的根底~~
参考资料
- TypeScript 文档 - 映射类型:https://www.typescriptlang.org/docs/handbook/2/mapped-types.html
- TypeScript 工具类型:https://www.typescriptlang.org/docs/handbook/utility-types.html