讲师中心 微信公众号
AI工具推荐 视频效率加速

TypeScript:在对象类型中定义可选且受限的键值

千枫同学_4387

千枫同学_4387

发布时间:2025-11-22 14:45:01

|

575人浏览过

|

来源于php中文网

原创

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 方法。一个直观但存在问题的尝试可能是这样:

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

当我们尝试创建一个 MyNewDictionaryProblematic 类型的对象时,如果只包含部分键,TypeScript 编译器会报错:

const dictionary: MyNewDictionaryProblematic = {
  [ABC.A]: { // 错误:类型 '{ D: { onClick: () => null; onCancel: () => null; }; }' 缺少类型 '{ D: { onClick: () => void; onCancel: () => void; }; E: { onClick: () => void; onCancel: () => void; }; F: { onClick: () => void; onCancel: () => void; }; }' 中的以下属性: 'E', 'F'
    [DEF.D]: {
      onClick: () => null,
      onCancel: () => null,
    }
  }
};

这个错误发生的原因是,默认情况下,映射类型会将其生成的所有属性都视为必需的。这意味着 MyNewDictionaryProblematic 期望 ABC.A、ABC.B、ABC.C 都存在,并且在 ABC.A 内部,又期望 DEF.D、DEF.E、DEF.F 都存在。这显然不符合我们“不强制所有键都存在”的需求。

解决方案:利用映射修饰符 ?

TypeScript 提供了映射修饰符(Mapping Modifiers),允许我们修改映射类型生成属性的特性。其中,? 修饰符用于将属性标记为可选。通过在映射类型中添加 ?,我们可以轻松解决上述问题。

Scrapfly SDK TypeScript
Scrapfly SDK TypeScript

使用 Scrapfly SDK(scrapfly-sdk)在 TypeScript 或 JavaScript 中进行网页抓取,用于用户请求抓取 URL、绕过反爬虫防护、渲染页面等场景。

下载

将 MyNewDictionaryProblematic 类型修改如下:

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

在这个修正后的 MyNewDictionary 类型定义中,我们为 pKey 和 eKey 都添加了 ? 修饰符。这意味着:

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

示例代码与验证

使用修正后的 MyNewDictionary 类型,我们可以灵活地创建对象,只包含我们需要的键,而不会触发编译错误:

// 修正后的类型定义
type MyNewDictionary = {
  [pKey in AbcTypes]?: {
    [eKey in DefTypes]?: {
      onClick: () => void;
      onCancel: () => void;
    }
  }
};

// 示例用法 1: 只包含部分 AbcTypes 键和部分 DefTypes 键
const dictionary1: MyNewDictionary = {
  [ABC.A]: {
    [DEF.D]: {
      onClick: () => console.log('A.D clicked'),
      onCancel: () => console.log('A.D cancelled'),
    },
    [DEF.E]: { // 可以在 ABC.A 下添加另一个 DEF 键
      onClick: () => console.log('A.E clicked'),
      onCancel: () => console.log('A.E cancelled'),
    }
  },
  [ABC.C]: { // 也可以只包含 ABC.C,而忽略 ABC.B
    [DEF.F]: {
      onClick: () => console.log('C.F clicked'),
      onCancel: () => console.log('C.F cancelled'),
    }
  }
};

// 示例用法 2: 甚至可以创建一个空对象 (虽然不常见,但类型上是允许的)
const emptyDictionary: MyNewDictionary = {};

// 示例用法 3: 访问可选属性时,需要进行空值检查
if (dictionary1[ABC.A] && dictionary1[ABC.A][DEF.D]) {
  dictionary1[ABC.A][DEF.D]?.onClick(); // 安全调用
}

// 尝试添加不在 AbcTypes 或 DefTypes 中的键会报错
// const invalidDictionary: MyNewDictionary = {
//   'Z': { // 错误: 'Z' 不可赋值给类型 'AbcTypes'
//     [DEF.D]: { onClick: () => {}, onCancel: () => {} }
//   }
// };

通过以上示例,我们可以看到 ? 修饰符的强大之处。它允许我们在保持类型安全的同时,极大地增加了对象结构的灵活性。

注意事项与总结

  1. 嵌套可选性: 在本例中,由于是嵌套的映射类型,我们需要在每一层都应用 ? 修饰符,以确保每一级的属性都是可选的。如果只在外层使用 ?,内层属性仍会是强制性的。
  2. Partial 的区别: Partial 是一个实用类型,它会使类型 T 的所有属性变为可选。虽然它也能实现属性可选,但对于像本例中这种需要基于特定类型动态生成键的场景,直接在映射类型中使用 ? 更为精确和灵活。Partial 适用于将一个已定义的完整接口或类型变为可选版本,而映射修饰符 ? 则是在定义新类型时直接控制属性的可选性。
  3. 类型推断: 当你使用这种可选类型时,TypeScript 的类型推断会很智能。访问可选属性时,TypeScript 会提醒你该属性可能为 undefined,因此需要进行空值检查,如 dictionary1[ABC.A]?.[DEF.D]?.onClick()。
  4. as const 的作用: 在原始问题中,ABC 和 DEF 被定义为 as const。这使得它们的属性值成为字面量类型(例如 'A' 而不是 string),这对于创建精确的联合类型 AbcTypes 和 DefTypes 至关重要,从而确保映射类型中的键是具体且有限的。

通过巧妙地运用 TypeScript 的映射类型和可选属性修饰符 ?,开发者可以构建出高度灵活且类型安全的对象结构。这种方法不仅解决了在特定键集合中定义非强制性属性的问题,也提升了代码的可读性和可维护性,是编写健壮 TypeScript 应用的重要技巧。

热门AI工具

更多
UP简历
UP简历 Hot

一款AI办公效率工具,主要用于基于AI技术的免费在线简历制作工具,适合需要提升相关任务效率的用户。

火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

音述AI
音述AI Hot

一款AI音频处理工具,主要用于音述AI是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

创客贴AI

创客贴AI是一款AI图像与设计工具,AI辅助的智能在线设计工具。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

相关专题

更多
TypeScript工程化开发与Vite构建优化实践
TypeScript工程化开发与Vite构建优化实践

本专题面向前端开发者,深入讲解 TypeScript 类型系统与大型项目结构设计方法,并结合 Vite 构建工具优化前端工程化流程。内容包括模块化设计、类型声明管理、代码分割、热更新原理以及构建性能调优。通过完整项目示例,帮助开发者提升代码可维护性与开发效率。

312

2026.02.13

TypeScript全栈项目架构与接口规范设计
TypeScript全栈项目架构与接口规范设计

本专题面向全栈开发者,系统讲解基于 TypeScript 构建前后端统一技术栈的工程化实践。内容涵盖项目分层设计、接口协议规范、类型共享机制、错误码体系设计、接口自动化生成与文档维护方案。通过完整项目示例,帮助开发者构建结构清晰、类型安全、易维护的现代全栈应用架构。

520

2026.02.25

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

391

2026.03.13

TypeScript 全栈开发进阶指南
TypeScript 全栈开发进阶指南

面向有 JavaScript 基础的开发者,深入讲解 TypeScript 的类型系统与全栈开发实践。

286

2026.06.03

TypeScript Node.js 全栈工程化与Monorepo架构实践
TypeScript Node.js 全栈工程化与Monorepo架构实践

本专题围绕 TypeScript 在 Node.js 全栈开发中的工程化实践展开,系统讲解 Monorepo 架构设计、包管理策略、模块复用机制以及服务端与前端统一类型系统的构建方法。通过真实项目案例,帮助开发者提升大型全栈项目的可维护性与协作效率。

658

2026.06.16

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

6779

2023.08.02

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

6184

2023.10.24

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

2298

2023.09.20

Vite 项目如何打包和部署
Vite 项目如何打包和部署

Vite项目打包部署专题汇集生产构建、产物预览、输出目录、资源路径、Nginx与GitHub Pages部署及常见白屏和路由404问题的已审核文章,帮助读者完成上线流程。

40

2026.10.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WebStorm 官方调试文档
WebStorm 官方调试文档

共0课时 | 0人学习

TypeScript 教程
TypeScript 教程

共19课时 | 6.7万人学习

TypeScript——十天技能课堂
TypeScript——十天技能课堂

共21课时 | 1.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn