新闻中心
TypeScript 中如何定义具有可选键的嵌套映射类型

本文探讨了在 TypeScript 中定义具有可选键的嵌套对象类型的方法。当需要创建一个对象,其键值来源于预定义的类型集合,但并非所有可能的键都必须存在时,传统的映射类型会导致编译错误。通过引入 TypeScript 的映射修饰符 `?`,我们可以轻松地将这些键标记为可选,从而实现更灵活和健壮的类型定义。
在 TypeScript 的类型系统中,我们经常需要定义那些结构复杂、键值具有特定约束的对象。一种常见的场景是,我们希望对象的键来源于一个预定义的字符串字面量类型集合,并且可能存在嵌套结构。然而,默认情况下,使用映射类型(Mapped Types)创建的属性都是必需的。当实际对象只包含部分键时,就会导致类型检查错误。
问题背景与初始尝试
假设我们定义了两组常量,分别代表不同的类型集合:
export const ABC = {
A: 'A',
B: 'B',
C: 'C',
} as const;
export const DEF = {
D: 'D',
E: 'E',
F: 'F',
} as const;
// 提取字面量类型
export type AbcTypes = (typeof ABC)[keyof typeof ABC]; // 'A' | 'B' | 'C'
export type DefTypes = (typeof DEF)[keyof typeof DEF]; // 'D' | 'E' | 'F'我们的目标是创建一个字典类型的对象 MyNewDictionary,它的第一层键必须是 AbcTypes 中的值,第二层键必须是 DefTypes 中的值,并且最内层包含 onClick 和 onCancel 方法。关键在于,我们不希望强制要求对象中包含所有可能的 AbcTypes 和 DefTypes 组合。
初次尝试的类型定义可能如下所示:
type MyNewDictionary = {
[pKey in AbcTypes]: { // pKey 必须是 AbcTypes 中的一个
[eKey in DefTypes]: { // eKey 必须是 DefTypes 中的一个
onClick: () => void;
onCancel: () => void;
}
}
};当我们尝试创建一个 MyNewDictionary 类型的实际对象,但只包含部分键时,TypeScript 编译器会报错:
const dictionary: MyNewDictionary = {
[ABC.A]: {
[DEF.D]: {
onClick: () => null,
onCancel: () => null,
}
}
};
// 错误信息示例:
// Type '{ D: { onClick: () => null; onCancel: () => null; }; }'
// is missing the following properties from type '{ D: { onClick: () => void; onCancel: () => void; }; E: { onClick: () => void; onCancel: () => void; }; F: { onClick: () => void; onCancel: () => void; }; }'
// with properties 'E', and 'F'.这个错误表明,尽管我们只指定了 ABC.A 下的 DEF.D,但由于 MyNewDictionary 的定义方式,TypeScript 期望 ABC.A 属性下必须包含 DEF.D、DEF.E 和 DEF.F 所有子属性。同样,如果只定义了 ABC.A,也会期望 ABC.B 和 ABC.C 也存在。
Avatar AI
AI成像模型,可以从你的照片中生成逼真的4K头像
92
查看详情
解决方案:映射修饰符 ?
TypeScript 提供了映射修饰符(Mapping Modifiers)来控制映射类型中属性的可选性或只读性。其中,? 修饰符用于将属性标记为可选。
要解决上述问题,我们只需要在映射类型中的键后面添加 ?,表示该属性是可选的。对于嵌套结构,我们需要在每一层需要可选的映射键后面都添加 ?。
type MyNewDictionary = {
[pKey in AbcTypes]?: { // 使 pKey (例如 'A', 'B', 'C') 成为可选属性
[eKey in DefTypes]?: { // 使 eKey (例如 'D', 'E', 'F') 成为可选属性
onClick: () => void;
onCancel: () => void;
}
}
};通过添加 ? 修饰符,我们修改了 MyNewDictionary 的行为:
- [pKey in AbcTypes]? 表示 MyNewDictionary 可以包含 AbcTypes 中的任意键,但不需要包含所有键。如果某个 pKey 不存在,也是合法的。
- 对于每个 pKey 对应的对象 { [eKey in DefTypes]?: ... },其内部的 [eKey in DefTypes]? 同样表示该对象可以包含 DefTypes 中的任意键,但不需要包含所有键。
现在,使用这个修正后的类型定义,我们可以灵活地创建 dictionary 对象,只包含我们需要的特定组合,而不会引发类型错误:
const dictionary: MyNewDictionary = {
[ABC.A]: {
[DEF.D]: {
onClick: () => null,
onCancel: () => null,
}
},
[ABC.C]: { // 也可以只定义 ABC.C
[DEF.F]: {
onClick: () => console.log('C.F click'),
onCancel: () => console.log('C.F cancel'),
}
}
};
// 甚至可以是一个空对象,如果没有任何键被定义
const emptyDictionary: MyNewDictionary = {};
// 也可以只定义一个顶层键,其内部为空对象(因为内层键也是可选的)
const partialDictionary: MyNewDictionary = {
[ABC.B]: {}
};
console.log(dictionary);
console.log(emptyDictionary);
console.log(partialDictionary);注意事项与总结
-
嵌套可选性: 当处理嵌套的映射类型时,如果希望内部属性也是可选的,必须在每一层映射键后都使用 ? 修饰符。Partial
实用类型只能对顶层属性
起作用,对于深层嵌套的属性,需要手动应用可选修饰符或递归地使用 Partial。 - 其他映射修饰符: 除了 ? (可选) 之外,TypeScript 还支持 readonly (只读) 修饰符。这些修饰符也可以与 + 或 - 符号结合使用,例如 +? 明确添加可选性,-? 明确移除可选性(使属性变为必需)。默认的 ? 行为等同于 +?。
- 类型安全与灵活性: 映射修饰符提供了一种强大的机制,可以在保持类型安全的同时,为对象结构提供极大的灵活性,特别适用于配置对象、API 响应处理等场景,这些场景中数据结构可能不总是完整的。
通过正确运用 TypeScript 的映射修饰符 ?,开发者可以精确地定义对象的结构,使其键值既能限定在特定类型集合内,又能按需选择性地存在,从而避免不必要的类型检查错误,并提升代码的健壮性和可维护性。
以上就是TypeScript 中如何定义具有可选键的嵌套映射类型的详细内容,更多请关注其它相关文章!
# app
# win
# 编译错误
# 可选
# 修饰符
# 递归
# typescript
# 服务端
# 怎样建模论文网站推广
# seo链接在哪里
# 郑州seo优化手段
# 阿里云建设企业网站
# CT营销推广准备
# 都是
# 但不
# 我们可以
# 键值
# 创建一个
# 数据结构
# seo 外链 源码
# 武穴seo优化口碑
# 线上推广营销怎么谈
# 莆田优化推广网站
# 抚顺企业seo
相关栏目:
【
科技资讯46185 】
【
网络学院92790 】
相关推荐:
文本文档写html代码怎么运行_文本文档html代码运行步骤【教程】
CSS响应式网页如何实现主次模块比例自适应_flex-grow与flex-shrink调整
AI泡沫首次被“刺破”:GPU十年都无法存活!
12306选座系统怎么选连座_12306选座多人连坐操作方法
怎么去除衣服上的口红印_生活小妙招教你用酒精轻松擦除
三星GalaxyZFold5怎样在相册制作折叠屏分镜_iPhone三星GalaxyZFold5相册制作折叠屏分镜【创意编辑】
必由学官方登录入口 必由学教师学生账号快速访问
新手怎么开始学化妆 零基础化妆入门教程
Windows 11怎么彻底关闭定位_Windows 11服务中禁用Geolocation
处理嵌套交互式控件:前端可访问性指南
C++如何比较两个字符串_C++ string compare函数与操作符对比
J*a应用程序首次运行自动创建文件与目录的最佳实践
在Pyomo中实现基于变量的条件约束:Big-M方法详解
PySpark中从现有列右侧提取可变长度字符创建新列的教程
Fabric模组开发:自定义物品与物品组的现代管理方法
魅族20怎样在浏览器开无图省流_iPhone魅族20浏览器开无图省流【流量节省】
PDF文件体积过大处理_PDF压缩技巧详解
Python字典中优雅地迭代剩余元素的方法
php源码怎么看淘宝客系统_看php源码淘宝客系统技巧
Yandex搜索引擎官方地址 俄罗斯网络世界的主要入口
铁路12306改签能改到更早的车次吗_铁路12306改签提前车次规则
Node.js CSV 数据处理:基于字段值条件过滤整条记录的策略
邮政快递单号查询入口 邮政快递物流信息在线查询入口
如何使用Rector自动化升级旧代码_通过Composer安装和配置Rector进行代码重构
cad如何更改注释性对象的比例_cad注释性比例调整方法
拼多多购物车商品数量无法修改如何处理 拼多多购物车操作优化方法
lar*el怎么安全地存储和获取配置文件中的敏感信息_lar*el敏感信息安全存储方法
PHP高效扁平化嵌套数组:使用array_merge与数组解包操作符
Win11怎么合并任务栏图标 Win11开启任务栏合并减少图标占空间【方法】
C++的std::forward_list怎么用_C++ STL中单向链表容器的特点与应用
J*aScript 字符串标签转换:使用正则表达式高效替换
Python多版本共存与虚拟环境管理深度指南
优化MinIO list_objects_v2 操作的性能瓶颈与最佳实践
126邮箱手机版登录官网2026_126手机邮箱免费入口最新
windows10怎么查看硬盘序列号_windows10硬盘id查询命令
php源码怎么在电脑上测试_电脑测试php源码方法步骤【教程】
修复二维数组索引越界异常:一维循环到二维坐标的正确映射
QQ邮箱正确登录入口_QQ邮箱官方网站使用地址
智慧团建扫码登录入口 智慧团建扫码登录入口官网版
Python实现多节点属性重叠度分析教程
抖音隐秘迷城小游戏入口_ 抖音冒险解谜小游戏秒玩
sublime如何只显示或隐藏特定类型文件_sublime侧边栏文件过滤
Basecamp怎样用留言钉固定重点_Basecamp用留言钉固定重点【重点标记】
Go与Ruby之间实现AES加密互通:CFB模式下的密钥长度匹配策略
Excel Power Pivot如何处理XML数据源 构建高级数据模型
QQ邮箱稳定登录入口_QQ邮箱官方网站网页版使用
理解J*aScript Promise的微任务队列与执行顺序
Pygame教程:解决用户输入与游戏状态更新不同步问题
汽水音乐在线解析 汽水音乐在线解析入口
荒野行动PC版怎么注册_荒野行动PC版账号注册详细流程图文教程


2025-11-22
浏览次数:次
返回列表
起作用,对于深层嵌套的属性,需要手动应用可选修饰符或递归地使用 Partial。