在TypeScript的世界里,类型不仅仅是静态的声明,它们也可以是动态的、可迭代的。通过TypeScript的映射类型(Mapping Types)特性,我们可以利用 in
关键字结合联合类型或索引签名,对已有的类型进行迭代和转换,从而创建出全新的类型定义。这一特性极大地增强了TypeScript在复杂数据结构处理中的灵活性和表达能力。
一、什么是映射类型?
映射类型是一种高级类型操作,它允许我们根据一个已存在的类型(称为“源类型”),通过某种规则(称为“映射规则”)来生成一个新的类型。这种操作类似于数组中的map
函数,但作用于类型层面,而非值层面。
二、如何使用in
关键字进行类型迭代?
在映射类型中,in
关键字扮演着核心角色。它允许我们遍历源类型的所有键(key),并对每个键应用一个转换规则,从而生成目标类型中的新属性。这种机制为处理对象类型或接口中的属性提供了极大的便利。
三、示例:转换对象属性的类型
假设我们有一个表示用户信息的接口,我们希望将接口中所有属性的类型从string
转换为number
(尽管这在现实中可能不常见,但仅作为示例)。
interface UserInfo {
id: string;
name: string;
age: string; // 假设这里年龄也被错误地声明为string
}
// 使用映射类型将UserInfo中所有string类型的属性转换为number类型
type NumberUserInfo = {
[K in keyof UserInfo as `${K}Number`]: number;
};
// 注意:这里使用了`as`语法进行键名重命名,并假设我们需要创建新属性名而非直接覆盖原属性类型
// 如果你只是想转换类型而不改变键名,可以省略`as`部分并直接在类型位置指定number
// 更接近实际用途的版本,不改变键名:
type ConvertedUserInfo = {
[K in keyof UserInfo]: number;
};
// 但请注意,直接这样转换可能不符合逻辑,因为id和name通常不是数字
// 这里只是为了演示映射类型的使用
根据上一篇我们学习《TS进阶之泛型分发》一起来思考泛型映射类型应该怎么做到递归?
比如说:
interface IInfo {
name: string;
age: number;
school: {
middleSchool: string;
highSchool: string;
university: string;
}
}
type OptionalInfo = Partial<IInfo>;
可以看到利用 Partial 关键字仅仅对于对象类型中的最外层进行了可选标记。如下图1-1所示
图1-1
但是对于内层嵌套类型比如 school 仍是一个对象类型,那么此时是无法深度进入 school 类型中进行标记的。
那么假如此时我有需求希望实现深度可选,应该如何做呢?大家可以查看 《TS进阶之泛型分发》提到过的条件判断和循环结合来考虑下。
interface IInfo {
name: string;
age: number;
school: {
middleSchool: string;
highSchool: string;
university: string;
};
}
// 其实实现很简单,首先我们在构造新的类型value时
// 利用 extends 条件判断新的类型value是否为 object
// 如果是 -> 那么我仍然利用 deepPartial<T[K]> 进行包裹递归可选处理
// 如果不是 -> 普通类型直接返回即可
type deepPartial<T> = {
[K in keyof T]?: T[K] extends object ? deepPartial<T[K]> : T[K];
};
type OptionalInfo = deepPartial<IInfo>;
let value: OptionalInfo = {
name: '1',
school: {
middleSchool:'xian'
},
};
四、映射类型的优势
- 提高代码复用性:通过映射类型,我们可以避免为每种类型转换场景编写独立的类型定义,减少了代码冗余。
- 增强类型安全性:TypeScript的编译器会在编译时检查映射类型的正确性,确保类型安全。
- 促进代码可维护性:当源类型发生变化时,映射类型会自动适应这些变化,减少了因类型不匹配而导致的错误。
五、实际应用场景
- 在使用Redux、Vuex等状态管理库时,定义action types和state shapes。
- 在构建复杂的数据结构时,如嵌套对象、联合类型等,通过映射类型简化类型定义。
- 在实现泛型库或框架时,提供灵活的类型转换功能。
六、结论
TypeScript的映射类型是一项强大的特性,它允许我们通过迭代和转换已有类型来创建新的类型定义。通过in
关键字的使用,我们可以轻松地遍历源类型的所有属性,并应用自定义的转换规则。掌握映射类型的使用,将使你在TypeScript项目中更加游刃有余地处理复杂的数据结构和类型需求。