TypeScript教程:使用映射类型和可选修饰符定义具有受限且可选键的对象


TypeScript教程:使用映射类型和可选修饰符定义具有受限且可选键的对象

本教程旨在解决在 typescript 中定义对象类型时遇到的一个常见问题:如何确保对象的键来源于一个预定义的集合,但同时允许这些键是可选的,而非全部强制存在。文章将深入探讨如何结合使用映射类型(mapped types)和可选修饰符(?),以创建灵活且类型安全的对象结构,从而避免因缺少非必需属性而导致的编译错误。

在 TypeScript 开发中,我们经常需要定义具有特定结构的对象,其中对象的键值(key)必须限定在某个预设的枚举或字符串集合中。然而,一个常见的挑战是,我们可能不希望对象必须包含该集合中的所有键,而是允许它们是可选的。本文将详细介绍如何利用 TypeScript 的映射类型(Mapped Types)和可选修饰符(Mapping Modifiers)来优雅地解决这一问题。

定义基础类型集合

首先,我们定义两个常量对象,它们将作为我们对象键的来源。使用 as const 可以确保 TypeScript 推断出最窄的字面量类型,而不是宽泛的 string 类型。

export const ABC = {
  A: 'A',
  B: 'B',
  C: 'C',
} as const;

export const DEF = {
  D: 'D',
  E: 'E',
  F: 'F',
} as const;

接下来,我们基于这些常量对象创建联合类型(Union Types),这些联合类型将精确地表示允许的键值。

export type AbcTypes = (typeof ABC)[keyof typeof ABC]; // 类型为 'A' | 'B' | 'C'
export type DefTypes = (typeof DEF)[keyof typeof DEF]; // 类型为 'D' | 'E' | 'F'

AbcTypes 和 DefTypes 现在分别是 ABC 和 DEF 对象中所有值组成的字面量联合类型。

初始尝试与遇到的问题

我们的目标是创建一个字典类型 MyNewDictionary,它的第一层键来自 AbcTypes,第二层键来自 DefTypes。每个最内层对象都包含 onClick 和 onCancel 两个函数。

一种直观的定义方式是使用映射类型:

type MyNewDictionaryAttempt = {
  [pKey in AbcTypes]: {
    [eKey in DefTypes]: {
      onClick: () => void;
      onCancel: () => void;
    }
  }
};

然而,当我们尝试创建一个 MyNewDictionaryAttempt 类型的对象实例,并且只赋值了部分键时,TypeScript 编译器会报错:

度加剪辑 度加剪辑

度加剪辑(原度咔剪辑),百度旗下AI创作工具

度加剪辑 380 查看详情 度加剪辑
const dictionaryAttempt: MyNewDictionaryAttempt = {
  [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; }; }'
*/

这个错误表明,尽管我们只为 ABC.A 下的 DEF.D 属性赋了值,但 MyNewDictionaryAttempt 类型要求 ABC.A 下必须包含 DEF.E 和 DEF.F,同样,整个 dictionaryAttempt 对象也必须包含 ABC.B 和 ABC.C。这是因为默认情况下,映射类型会创建所有属性都为必需(mandatory)的新类型。

解决方案:使用可选修饰符 ?

要解决这个问题,我们需要引入 TypeScript 的映射修饰符(Mapping Modifiers)。具体来说,使用 ? 修饰符可以将映射类型生成的属性标记为可选(optional)。

我们将 MyNewDictionaryAttempt 类型修改为 MyNewDictionary,在每个映射类型的键后面添加 ?:

type MyNewDictionary = {
  [pKey in AbcTypes]?: { // 外层键现在是可选的
    [eKey in DefTypes]?: { // 内层键现在也是可选的
      onClick: () => void;
      onCancel: () => void;
    }
  }
};

通过在 [pKey in AbcTypes] 和 [eKey in DefTypes] 后面分别添加 ?,我们告诉 TypeScript:

  1. MyNewDictionary 对象可以包含 AbcTypes 中的任意键,但不需要全部包含。
  2. 对于 AbcTypes 中的每个键(例如 ABC.A),其对应的值(一个对象)可以包含 DefTypes 中的任意键,但同样不需要全部包含。

现在,我们可以按照预期创建对象实例,只包含我们需要的属性,而不会引发编译错误:

const dictionary: MyNewDictionary = {
  [ABC.A]: {
    [DEF.D]: {
      onClick: () => console.log('A.D clicked'),
      onCancel: () => console.log('A.D cancelled'),
    },
    // DEF.E 和 DEF.F 在这里是可选的,可以不写
  },
  [ABC.C]: { // ABC.B 也是可选的,可以不写
    [DEF.F]: {
      onClick: () => console.log('C.F clicked'),
      onCancel: () => console.log('C.F cancelled'),
    }
  }
};

// 尝试访问存在的属性
if (dictionary[ABC.A]?.[DEF.D]) {
  dictionary[ABC.A][DEF.D]?.onClick(); // 输出: A.D clicked
}

// 尝试访问不存在的属性(类型安全地处理 undefined)
console.log(dictionary[ABC.B]?.D?.onClick); // 输出: undefined

总结与注意事项

  • 映射类型 (Mapped Types): 允许你基于现有类型创建新类型,通过遍历一个联合类型或字面量类型的所有成员来生成新的属性。语法是 {[Key in UnionType]: ValueType}。
  • 可选修饰符 (?): 在映射类型中使用 ? ({[Key in UnionType]?: ValueType}) 可以将生成的属性标记为可选。这意味着在创建该类型的对象时,这些属性可以被省略。
  • as const 的重要性: 使用 as const 确保 TypeScript 推断出最具体的字面量类型,这对于创建精确的联合类型(如 AbcTypes)至关重要,从而使映射类型能够正确地工作。
  • 嵌套可选性: 当处理嵌套对象时,如果希望内层属性也是可选的,需要在内层映射类型中也使用 ? 修饰符。
  • 其他映射修饰符: 除了 ? (可选),还有 -? (必需),readonly (只读),和 -readonly (可写) 等修饰符,可以根据需求灵活组合使用。

通过掌握映射类型和可选修饰符,你可以在 TypeScript 中创建出更加灵活、健壮且类型安全的对象结构,有效管理复杂的配置或数据字典,同时避免不必要的强制性属性检查。

以上就是TypeScript教程:使用映射类型和可选修饰符定义具有受限且可选键的对象的详细内容,更多请关注其它相关文章!


# 不写  # 栾城网站推广效果  # 天津推广关键词排名优化  # 如何推广网站选火21星  # 鄞州区外贸推广网站优化  # 服务网站推广技术  # 网站建设的功能定位  # 临沧营销推广咨询招聘信息  # 廊坊网站如何做推广  # 网站建设中html5  # 小企业网站优化  # 这一  # 有什么特点  # typescript  # 如何实现  # 键值  # 创建一个  # 如何使用  # 有什么关系  # 修饰符  # 可选  # typescript教程  # 编译错误  # 常见问题  # win  # app 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894


相关推荐: 高效调试PHP大型嵌套数组:JSON序列化与可视化工具实践  大众点评了却看不到是怎么回事  漫蛙manwa官网浏览入口_漫蛙漫画网页版访问链接  POKI小游戏在线免费入口链接 POKI小游戏无下载秒玩玩  win11关机几秒又自己开机 Win11关机自动重启问题修复  CDR如何复制交互式填充色  MacBook Pro词典使用指南  Python csv 模块处理非字符串数据:列表写入 CSV 文件的机制解析  在J*a中如何实现类的继承与方法重用_OOP继承方法重用技巧分享  行者app怎样导出日志  济南公交卡手机充值指南  PHP魔术方法__set与__isset:设计考量、性能权衡与静态分析的视角  易车网官网直达入口 易车网在线登录入口  实现可重用自定义Python Range类  智云Q3和Q2有什么升级_智云Q3与Q2手持云台功能与性能对比分析  iPhone14无法连接蓝牙设备如何解决  如何在Podman容器中运行Composer_Docker替代品Podman的PHP与Composer容器化实践  安居客移动经纪人怎么设置自动回复?-安居客移动经纪人设置自动回复的方法  怎样设置开机后自动运行某个程序_Windows启动文件夹与任务计划【自动化】  Golang如何使用crypto/md5生成哈希_Golang MD5哈希生成方法  B站怎么开|直播| B站|直播|申请需要什么条件【新手必看】  狙击外星人小游戏在线链接_狙击外星人小游戏网页链接  QQ邮箱手机版网页版 QQ邮箱登录入口地址  Golang如何使用gRPC拦截器实现日志收集_Golang gRPC拦截器日志收集实践  悟空浏览器网页版在线工具 悟空浏览器网页版在线平台入口  实现二叉树的层序插入:基于树大小的路径导航  win11怎么更改账户类型 Win11标准用户和管理员权限切换【教程】  抖音猜你想搜能说明对方搜过吗  胃动力不足?试试这5个调理方法  rabbitmq 持久化有什么缺点?  使用CSS :has() 选择器实现父元素样式控制:从子元素反向应用样式  《星露谷物语》克林特好感度事件介绍  服装短视频如何起号推广?服装短视频起号推广有什么要求?  抖音火山版注销账号抖音会注销吗 抖音火山版与抖音账号注销关系  招商淘客入门指南  Lar*el Dusk 测试中管理浏览器权限:以剪贴板访问为例  c++如何链接Boost库_c++准标准库的集成与使用  《崩坏:星穹铁道》3.6版本异相仲裁打法及配队推荐  动漫岛汉化官网网 动漫岛官方动漫汉化地址  Firefox OS应用开发:解决XMLHttpRequest跨域请求阻塞问题  电脑从睡眠中被自动唤醒怎么办_Windows唤醒源事件查看与禁用【解决】  嘀嗒顺风车如何开具电子发票  使用Python和NLTK从文本中高效提取名词的实用教程  Golang如何实现HTTP请求重试机制_Golang HTTP请求错误处理策略  我的世界游戏平台入口 我的世界官方官网直达链接  店铺如何做视频号推广?做视频号推广有用吗?  快递优选如何查优选物流_快递优选专属物流渠道查询与配送时效  《浙里办》电子发票开具方法  圆通快递官网入口查询单号 手机版官方查询入口  《深林》冬季章节图文攻略 

 2025-11-22

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.