文章目录
类型映射基于索引类型,将一种类型按照映射规则,转换成另一种类型,通常用于对象类型。
注意:类型映射只能与类型别名一起使用。
映射基本语法
场景:基于旧类型创建新类型(对象类型),减少重复、提升开发效率。
例1:使用 in 关键字( 类似 for in )遍历联合类型 'x' | 'y' | 'z',循环创建 x,y,z 属性并设置类型注解 number。通过类型映射创建的新对象类型 Type1 和类型 Type2 结构完全相同。
type PropKeys = 'x' | 'y' | 'z';
// 使用类型映射创建类型Type1
type Type1 = { [Key in PropKeys]: number }
// 等同于
type Type2 = { x: number; y: number; z: number}
例1:下面示例中,先执行 keyof Props 获取对象类型 Props 中所有键的联合类型即 'a' | 'b'| 'c' ,再通过 in 关键字(类似 for in)遍历联合类型,循环创建 a,b,c 属性并设置类型注解 number。通过类型映射创建的新对象类型 Type3 和类型 Type4 结构完全相同。
// 创建对象类型
type Props = { a: number; b: string; c: boolean }
// 通过映射创建新类型
type Type3 = { [key in keyof Props]: number }
// 等同于
type Type4 = {
a: number;
b: number;
c: number;
}
Partial<Type> 就是基于类型映射实现的。
- keyof T 即 keyof Props 表示获取 Props 的所有键,也就是:'a' | 'b' | 'c'。
- 在 [] 后面添加 ?(问号),表示将这些属性变为可选的,以此来实现 Partial 的功能。
- 冒号后面的 T[P] 表示获取 T 中每个键对应的类型。比如,如果是 'a' 则类型是 number;如果是 'b' 则类型是 string。
- 最终,新类型 PartialProps 和旧类型 Props 结构完全相同,只是让所有类型都变为可选了。
type Props = { a: number; b: string; c: boolean }
type PartialProps = Partial<Props>
Partial 底层代码
映射修饰符
TS 引入了两个映射修饰符,用来在映射时添加或移除某个属性的 ? 修饰符和 readonly 修饰符。
- + 修饰符:写成 +? 或 +readonly,为映射属性添加 ? 修饰符或 readonly 修饰符。可以省略+
- – 修饰符:写成 –? 或 –readonly,为映射属性移除 ? 修饰符或 readonly 修饰符。
type Props = {
a: number;
b?: string;
readonly c: boolean;
}
// 所有属性添加可选修饰符 +
type Type5 = {
[key in keyof Props]+?: Props[key]
}
// 所有属性移除可选修饰符 -
type Type6 = {
[key in keyof Props]-?: Props[key]
}
// 所有属性添加只读修饰符 readonly
type Type7 = {
+readonly [key in keyof Props]: Props[key]
}
// 所有属性移除只读修饰符 readonly
type Type8 = {
-readonly [key in keyof Props]: Props[key]
}
所有属性添加可选修饰符 +
所有属性移除可选修饰符 -
所有属性添加只读修饰符 readonly
所有属性移除只读修饰符 readonly
键名重映射
由于键名重映射可以修改键名类型,所以原始键名的类型不必是 string | number | symbol,任意的类型都可以用来进行键名重映射。
【更改键名】
interface Person {
name: string;
age: number;
location: string;
}
type Getters<T> = {
[P in keyof T
as `get${Capitalize<string & P>}`]: () => T[P];
};
type LazyPerson1 = Getters<Person>;
// 等同于
type LazyPerson2 = {
getName: () => string;
getAge: () => number;
getLocation: () => string;
}
解释:
- P in keyof T:获取类型 T 的每一个属性。
- as:修改键名。
- get:为键名添加的前缀。
- Capitalize<T>:一个原生的工具泛型,用来将T的首字母变成大写。
- string & P:一个交叉类型,其中的P是 keyof 运算符返回的键名联合类型string|number|symbol,但是 Capitalize<T> 只能接受字符串作为类型参数,因此 string & P 只返回 P 的字符串属性名。
【属性过滤】
type User = {
name: string,
age: number
}
type Filter<T> = {
[K in keyof T as T[K] extends string ? K : never]: string
}
type FilteredUser = Filter<User> // { name: string }
解释:
- K in keyof T:获取类型 T 的每一个属性。
- as:修改键名。
- T[K] extends string ? K : never]:使用了条件运算符,如果属性值他 T[K] 的类型是字符串,那么属性名不变,否则属性名类型改为 never,即这个属性名不存在。这样就等于过滤了不符合条件的属性,只保留属性值为字符串的属性。
【映射联合类型】
type S = {
kind: 'square',
x: number,
y: number,
};
type C = {
kind: 'circle',
radius: number,
};
type MyEvents<Events extends { kind: string }> = {
[E in Events as E['kind']]: (event: E) => void;
}
type Config1 = MyEvents<S|C>;
// 等同于
type Config2 = {
square: (event:S) => void;
circle: (event:C) => void;
}
解释:
- E in Events:Events 是两个对象组成的联合类型 S | C,所以 E 是一个对象。
- as E['kind']:修改键名。
- Events extends { kind: string }:传入的泛型参数必须有 kind 属性,且为 string 类型。
- S|C:对象组成的联合类型。