新闻中心

TypeScript 教程:在映射类型中实现可选且受限的对象键

2025-11-22
浏览次数:
返回列表

TypeScript 教程:在映射类型中实现可选且受限的对象键

本教程探讨如何在 typescript 中定义一个对象类型,使其键值受限于预定义集合,同时允许这些键是可选的,而非强制。通过利用 typescript 的映射修饰符 `?`,我们能够灵活地构建嵌套对象类型,避免因缺少部分键而导致的类型错误,从而提升类型定义的灵活性和实用性。

在 TypeScript 中定义复杂的数据结构时,我们经常需要创建一种对象类型,其键名必须来自某个预定义的集合,但同时又希望这些键是可选的,即不必全部存在。这在构建配置对象、API 响应或灵活的数据字典时尤为常见。本文将深入探讨如何使用 TypeScript 的映射类型(Mapped Types)及其修饰符来优雅地解决这一问题。

1. 问题背景与初始尝试

假设我们有两组常量,分别定义了两种类型的标识符:

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 两个函数。

最初的尝试可能会是这样:

type MyNewDictionary = {
  [pKey in AbcTypes]: {
    [eKey in 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; }; }': 'E', 'F'
// 并且外层也可能报错,因为缺少 'B', 'C'

这个错误表明,当前 MyNewDictionary 的定义强制要求所有 AbcTypes 和 DefTypes 中的键都必须存在。使用 Partial 作用于整个 MyNewDictionary 类型可能无法完全解决嵌套层级的可选性问题,因为它只会使顶层属性可选,而嵌套的映射类型内部仍然是强制的。

2. 解决方案:映射修饰符 ?

TypeScript 提供了强大的映射修饰符来改变映射类型中属性的特性。其中,? 修饰符用于将属性标记为可选(Optional)。通过在映射类型中直接应用 ?,我们可以精确控制哪些属性是可选的。

Avatar AI Avatar AI

AI成像模型,可以从你的照片中生成逼真的4K头像

Avatar AI 92 查看详情 Avatar AI

将 ? 修饰符应用于上述 MyNewDictionary 的定义,可以使其内部的键变为可选:

type MyNewDictionaryCorrected = {
  [pKey in AbcTypes]?: { // 使第一层键可选
    [eKey in DefTypes]?: { // 使第二层键可选
      onClick: () => void;
      onCancel: () => void;
    }
  }
};

在这个修正后的类型定义中:

  • [pKey in AbcTypes]?:表示 MyNewDictionaryCorrected 的第一层属性,其键名来自 AbcTypes,但这些属性是可选的。
  • [eKey in DefTypes]?:表示内层对象的属性,其键名来自 DefTypes,同样是可选的。

3. 完整示例与验证

现在,使用 MyNewDictionaryCorrected 类型,我们可以创建只包含部分键的对象,而不会遇到类型错误:

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];
export type DefTypes = (typeof DEF)[keyof typeof DEF];

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

// 示例 1:只包含一个顶层键和一个内层键
const dictionary1: MyNewDictionaryCorrected = {
  [ABC.A]: {
    [DEF.D]: {
      onClick: () => console.log('A.D onClick'),
      onCancel: () => console.log('A.D onCancel'),
    }
  }
};
console.log(dictionary1); // 类型检查通过

// 示例 2:包含多个顶层键,但每个顶层键下只包含部分内层键
const dictionary2: MyNewDictionaryCorrected = {
  [ABC.A]: {
    [DEF.D]: { onClick: () => {}, onCancel: () => {} },
    [DEF.F]: { onClick: () => {}, onCancel: () => {} },
  },
  [ABC.C]: {
    [DEF.E]: { onClick: () => {}, onCancel: () => {} },
  }
};
console.log(dictionary2); // 类型检查通过

// 示例 3:尝试使用非定义的键,会报错
// const invalidDictionary: MyNewDictionaryCorrected = {
//   'X': { // 错误:类型 '"X"' 不可分配给类型 'AbcTypes'
//     [DEF.D]: { onClick: () => {}, onCancel: () => {} }
//   }
// };

通过这个例子,我们可以看到 MyNewDictionaryCorrected 成功地实现了我们的需求:键名受限于 AbcTypes 和 DefTypes,但它们都是可选的。

4. 总结与注意事项

  • 映射修饰符的力量:? 是 TypeScript 映射类型中非常有用的修饰符,它允许我们精确地控制属性的可选性。除了 ?,还有 readonly 修饰符(以及它们的逆操作 -? 和 -readonly),用于控制属性的只读性。
  • Partial 与映射修饰符:虽然 Partial 可以使一个类型的所有顶层属性变为可选,但对于嵌套的映射类型,直接在映射类型定义中应用 ? 提供了更细粒度的控制,尤其是在我们希望某些层级保持可选,而其他层级仍是强制时。在本文的场景中,由于是嵌套的映射类型,直接在每个映射表达式后添加 ? 是最直接有效的方案。
  • 类型安全与灵活性:这种方式在保证类型安全的同时,极大地提升了对象定义的灵活性。我们既能限制允许的键集,又能避免强制所有键都必须存在的冗余。

掌握映射类型及其修饰符是 TypeScript 高级用法中的重要一环,它能帮助我们构建出更健壮、更灵活且易于维护的代码。

以上就是TypeScript 教程:在映射类型中实现可选且受限的对象键的详细内容,更多请关注其它相关文章!


# 中非  # 淘宝店铺Seo案例  # 圣诞节怎么做营销推广  # 舟山抖音seo厂家  # 桂林本地seo策略  # 番禺网站优化服务  # 市场营销推广策划案例  # 抖音seo推广报价多少  # 摄影网站推广公司  # 天猫网店营销推广侧重点  # 太仓营销推广报价  # 服务端  # typescript  # 使其  # 我们可以  # 第一层  # 键名  # 报错  # 数据结构  # 修饰符  # 可选  # win  # app 


相关栏目: 【 科技资讯46185 】 【 网络学院92790


相关推荐: css滚动区域卡顿如何改善_css滚动问题用will-change优化渲染  Django表单验证失败时保留用户输入数据的最佳实践  Google翻译怎么语音输入_Google翻译语音输入功能使用与设置方法  腾讯QQ邮箱官方网站_QQ邮箱网页版在线登录  outlook中文官网入口地址 outlook官方中文版直达首页链接  J*aScript类型检查_j*ascript代码规范  Node.js中HTML按钮与J*aScript函数交互的正确姿势  痛风发作了怎么办? 快速止痛和后期饮食调理  火锅吃太多会怎样 火锅吃太多会上火吗  高德地图公交到站提醒失败如何解决 高德提醒权限设置  处理嵌套交互式控件:前端可访问性指南  Win11如何使用Windows Sandbox Win11沙盒功能开启与使用教程【详解】  MAC如何将整个网页截长图_MAC使用Safari的导出为PDF或第三方工具  Android Studio计算器C键逻辑错误排查与修复:条件判断优化指南  HuggingFaceEmbeddings中向量嵌入维度调整的限制与理解  NetBeans Ant项目:自动化将资源文件复制到dist目录的教程  LocoySpider如何部署到云服务器_LocoySpider云部署的远程配置  包子漫画官方网站在线链接-包子漫画在线阅读平台主页地址  Win11怎么用U盘重装系统 Win11制作启动盘并重装系统完整教程【详解】  飞书妙记怎样用语音转文字速记_飞书妙记用语音转文字速记【速记方法】  红果短剧网页版官网入口 官方最新网址发布  如何在Promise链中有效终止错误处理后的执行  抖音从哪里进入网页版_抖音官方入口链接  移动端XML文件怎么转换成Excel 手机和平板上的解决方案  顺丰快递查询系统 官方正版查询入口  如何创建没有密码的Windows本地账户_跳过微软账户登录的技巧【教程】  想当下一个《2077》?《心之眼》Steam评价升至"多半好评"  163邮箱官方主页登录 直达网易邮箱登录核心页面  steam官方入口大全 steam账号注册及操作指南  Golang指针如何与map组合使用_Golang map指针组合实践  海棠电脑版入口_通过电脑访问海棠官网阅读  PowerPoint如何制作滚动字幕结尾彩蛋_PowerPoint路径动画实现平滑滚动字幕效果  Mac怎么查看崩溃日志_Mac控制台错误报告分析  地铁跑酷免费秒玩入口链接 地铁跑酷小游戏免费秒玩网站  如何使用纯J*aScript判断Input元素是否在特定类容器内  怎样在Excel中做仪表盘_Excel仪表盘设计与关键指标展示方法  jQuery Mask 插件中实现电话号码固定前导零的教程  qq邮箱日历功能怎么用_创建日程与会议邀请的技巧  俄罗斯搜索引擎Yandex指南 附2025年免登录官网入口  智慧团建扫码登录入口 智慧团建扫码登录入口官网版​  J*aScript数据结构转换:将对象数组按类别分组  c++如何使用Catch2编写单元测试_c++简洁易用的BDD风格测试框架  俄罗斯Yandex搜索引擎入口_Yandex官网免登录一键访问  Win10如何清理注册表垃圾 Win10手动清理无效注册表【技巧】  单射、满射与双射的关系 一文理清所有逻辑  百度浏览器字体显示异常偏小_百度浏览器字体渲染修复方案  html怎么运行外部js文件中的函数_运html外js文件函数法【技巧】  微博网页版官方账号登录 微博网页版内容浏览使用指南  Win10如何恢复误删的快捷方式_Win10重建常用软件快捷方式  Typer应用中动态命令行参数的解析与处理 

搜索