ES2020核心特性实战指南:可选链、空值合并与BigInt落地
1. 项目概述:ES2020不是“又一个版本”,而是你日常代码里悄悄变轻的那几行
我第一次在生产环境里用上?.操作符,不是因为看了什么技术大会PPT,而是某天凌晨三点改一个埋点上报逻辑——后端接口字段结构突然嵌套深了一层,前端没做兜底,整个用户行为数据链路断了两小时。回滚代码、加判断、再上线,折腾完天都亮了。第二天晨会,我顺手把所有类似位置的obj && obj.user && obj.user.profile && obj.user.profile.name全替成了obj?.user?.profile?.name,测试通过,打包体积没变,但代码清爽得像刚洗过的玻璃。这就是 ES2020 给我的第一课:它不炫技,不造新范式,只默默帮你把那些写过一百遍、改过一千次、review时总被提醒“这里要加空值判断”的防御性胶水代码,压成一行可读、可维护、且浏览器原生支持的语法糖。
ES2020(正式名称 ECMAScript 2020)是 JavaScript 语言标准在2020年6月正式发布的第11版。它不像 ES6 那样带来模块化、箭头函数这种结构性变革,也不像 ES2015 那样彻底重写开发体验;它的定位非常务实——解决开发者日复一日在真实项目中反复踩坑的“小痛点”。空值访问、大数计算、动态导入、全局错误捕获……这些不是实验室里的玩具特性,而是你在写表单校验、处理金融金额、做微前端路由、排查线上白屏时,真正需要伸手就能用的工具。它面向的不是“想学新语法”的人,而是“今天就要修这个bug”的人。关键词里提到的Towards AI — Multidisciplinary Science Journal这类平台,恰恰说明 ES2020 的价值已溢出纯前端圈层——当AI工程团队用 JavaScript 写数据预处理脚本、用 WebAssembly 跑模型推理时,BigInt和Promise.allSettled就不再是语法糖,而是精度与可靠性的基础设施。这篇文章不讲标准文档里的定义,只讲我在电商中台、SaaS后台、IoT设备管理平台三个不同场景下,如何把 ES2020 的7个核心特性,变成每天节省15分钟、减少3个潜在bug、让Code Review通过率提升20%的实操武器。
2. 核心特性深度拆解:为什么这7个特性值得你立刻停下手头工作去试
ES2020 官方共纳入9项提案,但其中两项(import.meta的url属性、String.prototype.matchAll)属于已有特性的补全或增强,实际对日常开发影响有限。真正能改变你编码肌肉记忆的,是以下7个特性。它们不是孤立存在的,而是一套协同工作的“防御性编程工具包”——从数据访问、数值计算、异步控制,到错误处理、模块加载,覆盖了现代 JavaScript 应用最常出问题的五个关键环节。我按实际使用频率和冲击力排序,并解释每个选择背后的工程权衡。
2.1 可选链操作符(Optional Chaining):终结“undefined is not an object”
?.操作符解决的是 JavaScript 最古老、最顽固的痛点:深层属性访问时的空值崩溃。传统写法如data.user.profile.avatar.url,只要中间任意一环是null或undefined,就会抛出TypeError。我们被迫写大量防御性代码:
// 旧写法:冗长、易错、可读性差 const avatarUrl = data && data.user && data.user.profile && data.user.profile.avatar && data.user.profile.avatar.url; // 或者用 try/catch(更糟,掩盖真实错误) let avatarUrl; try { avatarUrl = data.user.profile.avatar.url; } catch (e) { avatarUrl = ''; }?.的本质是短路求值的语法糖,但它比&&更精准:&&在遇到 falsy 值(如0,'',false)时也会短路,而?.只在null或undefined时才停止访问。这意味着obj?.count在count是0时仍会返回0,而不是undefined——这符合绝大多数业务逻辑对“零值有效”的预期。
提示:
?.不仅用于属性访问,还支持方法调用(obj?.method?.())和数组索引(arr?.[0])。但要注意,?.不能用于赋值左侧(obj?.prop = value会报错),也不能与delete操作符连用(delete obj?.prop无效)。
我在一个实时库存系统中应用它:后端返回的 SKU 数据结构随促销活动动态变化,sku.inventory?.availableStock成为标准写法。上线后,因字段缺失导致的Cannot read property 'availableStock' of undefined错误下降了92%。这不是靠加更多 if 判断实现的,而是靠语言原生能力消除了错误发生的土壤。
2.2 空值合并操作符(Nullish Coalescing):告别||的隐式类型转换陷阱
??操作符常与?.搭配出现,但它解决的是另一个维度的问题:提供默认值。很多人习惯用||:
// 危险!0, '', false 都会被当作“空”而取默认值 const count = response.data?.count || 0; // 当 count 是 0 时,结果却是 0?不,是 0 || 0 = 0,看似正确,但逻辑脆弱 const name = user?.name || 'Anonymous'; // 当 name 是 '' 时,得到 'Anonymous',但业务上空字符串可能就是有效状态||的问题是它基于falsy 值判断,而 JavaScript 中有7个 falsy 值(false,0,-0,0n,'',null,undefined,NaN)。??则严格得多:只在左侧操作数为null或undefined时,才返回右侧操作数。其他所有值,包括0,'',false,都被视为有效值而直接返回。
// 安全!语义清晰:只有真·空值才用默认值 const count = response.data?.count ?? 0; // count 是 0 → 返回 0;count 是 undefined → 返回 0 const name = user?.name ?? 'Anonymous'; // name 是 '' → 返回 '';name 是 null → 返回 'Anonymous'我在一个配置中心前端中强制推行??。后端返回的config.timeout字段,0表示“禁用超时”,undefined表示“使用系统默认值”。用||会导致0被错误地替换为默认值,引发严重超时问题。切换到??后,配置逻辑的语义完全对齐了业务需求。
2.3 BigInt:JavaScript 终于能精确表示 2^53 以上的整数了
JavaScript 的Number类型基于 IEEE 754 双精度浮点数,安全整数范围是-(2^53 - 1)到2^53 - 1(即±9007199254740991)。超过此范围,整数精度丢失:
console.log(9007199254740991 + 1); // 9007199254740992 ✅ console.log(9007199254740991 + 2); // 9007199254740992 ❌ 丢失了 +2 console.log(123456789012345678901234567890n + 1n); // 123456789012345678901234567891n ✅BigInt通过在数字后加n创建(如123n,0xffn),它是一种全新的原始类型,支持所有数学运算符(+,-,*,/,%,**),但不支持与Number类型混合运算(10n + 5会报错),也不能用于Math对象方法(Math.pow(10n, 2)无效)。
注意:
BigInt不能使用===与Number比较(10n === 10为false),必须用==(10n == 10为true)或先转换类型(Number(10n) === 10)。但在比较大小时,><等关系运算符可以跨类型使用(10n > 5为true)。
我在一个区块链浏览器项目中重度依赖BigInt。以太坊的区块高度、Gas 费用、Token 余额动辄是几十位的整数。过去用Number处理,balance.toString()会输出科学计数法(1.23e+25),UI 上显示为12300000000000000000000000,但最后几位其实是0——这是精度丢失的视觉表现。改用BigInt后,余额显示、转账计算、Gas 估算全部回归精确。更重要的是,BigInt支持toString(16)直接转十六进制,与智能合约 ABI 编码无缝对接。
2.4 Promise.allSettled:异步世界的“尽最大努力”策略
Promise.all是经典工具,但它有一个致命缺陷:只要有一个 Promise reject,整个all就立即 reject,其他仍在运行的 Promise 结果全部丢失。这在需要“收集所有请求结果,无论成功失败”的场景下极其不利,比如批量提交订单、并行获取多个微服务数据、上报多条日志。
Promise.allSettled彻底改变了这一点。它返回一个 Promise,该 Promise总是 fulfilled,其结果是一个数组,每个元素都是一个对象,包含status('fulfilled'或'rejected')以及对应的value或reason。
const promises = [ fetch('/api/user'), fetch('/api/orders'), fetch('/api/settings') ]; // Promise.all:一个失败,全盘皆输,无法知道其他两个是否成功 Promise.all(promises) .then(results => console.log('All succeeded:', results)) .catch(error => console.log('One failed, all lost:', error)); // Promise.allSettled:稳如老狗,结果尽在掌握 Promise.allSettled(promises) .then(results => { const successful = results.filter(r => r.status === 'fulfilled').map(r => r.value); const failed = results.filter(r => r.status === 'rejected').map(r => r.reason); console.log('Successful:', successful); console.log('Failed:', failed); });我在一个 SaaS 平台的“一键同步”功能中使用它。该功能需同时向 CRM、ERP、BI 三个系统推送客户数据。allSettled让我能清晰告诉用户:“CRM 同步成功,ERP 因网络超时失败(错误码503),BI 因权限不足失败(错误码403)”,而不是简单粗暴地提示“同步失败,请重试”。这极大提升了运维效率和用户体验。
2.5 globalThis:统一的全局对象访问入口
JavaScript 在不同宿主环境中,全局对象名称不同:浏览器是window,Node.js 是global,Web Worker 是self,甚至某些嵌入式环境是this。这导致跨环境代码(如通用工具库、Polyfill)必须写一堆判断:
// 旧写法:丑陋且易漏 const getGlobal = () => { if (typeof window !== 'undefined') return window; if (typeof global !== 'undefined') return global; if (typeof self !== 'undefined') return self; throw new Error('Unable to locate global object'); }; const globalObj = getGlobal();globalThis是一个标准化的、在任何 JavaScript 环境中都指向当前全局对象的属性。它不是一个变量,而是一个只读属性,存在于全局作用域。
// 新写法:干净、统一、未来-proof console.log(globalThis); // 浏览器中是 window,Node 中是 global,Worker 中是 self globalThis.myLib = { version: '1.0' }; // 在任何环境都能挂载到全局我在开发一个开源的 UI 组件库时,用globalThis替换了所有window.xxx的引用。这使得组件库不仅能跑在浏览器,还能无缝集成到 Electron、React Native WebView、甚至 Deno 环境中。globalThis的价值不在于它多强大,而在于它终结了“环境适配”的琐碎劳动。
2.6 动态 import():按需加载的终极形态
import()函数(注意:是函数调用,不是声明)允许你在运行时、条件分支中、甚至循环内部,动态地导入模块。它返回一个 Promise,解决了静态import无法满足的三大场景:条件加载(如根据用户权限加载管理后台)、路由懒加载(React Router/Vue Router 的基石)、以及减小首屏体积(将非关键代码分割出去)。
// 静态 import:编译时确定,无法条件化 // import { heavyModule } from './heavy.js'; // 动态 import:运行时决定,灵活无比 if (user.hasAdminRole()) { const { AdminPanel } = await import('./admin-panel.js'); render(AdminPanel); } // 或者在事件处理器中 button.addEventListener('click', async () => { const { Chart } = await import('./charting-library.js'); initChart(Chart); });import()的核心优势在于与构建工具(Webpack、Vite、Rollup)深度集成。当你写import('./module.js'),构建工具会自动将module.js及其依赖打包成一个独立的 chunk 文件(如chunk.abc123.js),并在运行时按需下载。这比手动fetch+eval安全无数倍,也比require.ensure(Webpack 2)等旧方案更标准、更易用。
我在一个大型企业级后台中,将所有报表模块、导出功能、第三方 SDK(如地图、支付)全部改为动态import()。首屏 JS 包体积从 2.1MB 降至 850KB,Lighthouse 性能评分从 42 提升至 89。更重要的是,用户点击“查看销售报表”时,才加载sales-report.js,而不是在首页就下载所有报表代码。
2.7 String.prototype.matchAll:正则匹配的“全量收割机”
String.prototype.match()方法在全局正则(/gflag)下,只返回匹配的字符串数组,丢失了捕获组(capturing groups)和索引信息。matchAll()则返回一个迭代器,每次迭代产生一个RegExpExecArray,其中包含完整的匹配信息:0是完整匹配,1..n是捕获组,还有index(匹配起始索引)和input(原字符串)。
const text = 'Contact us at support@example.com or sales@example.com'; const regex = /(\w+)@(\w+\.\w+)/g; // match():只给字符串,丢了邮箱用户名和域名的分组 console.log(text.match(regex)); // ['support@example.com', 'sales@example.com'] // matchAll():给你一切,结构化、可编程 const matches = [...text.matchAll(regex)]; matches.forEach(match => { console.log(`Full: ${match[0]}, User: ${match[1]}, Domain: ${match[2]}, Index: ${match.index}`); }); // Full: support@example.com, User: support, Domain: example.com, Index: 14 // Full: sales@example.com, User: sales, Domain: example.com, Index: 40matchAll()的价值在于它让正则从“文本提取工具”升级为“结构化解析引擎”。我在一个日志分析平台中用它解析 Nginx access log。每条日志格式固定,但字段间用空格分隔,且部分字段含空格(如User-Agent)。用matchAll配合精心设计的正则,能一次性提取出ip,timestamp,method,path,status,userAgent等12个字段,准确率100%,性能比split(' ')+ 复杂逻辑判断快3倍。
3. 实操落地指南:从 Babel 配置到生产环境避坑全记录
光知道特性不够,真正落地时,你会遇到构建工具兼容性、TypeScript 类型、浏览器支持、以及那些文档里不会写的“灰色地带”。下面是我踩过坑、验证过的完整实操路径,覆盖开发、测试、上线全流程。
3.1 构建工具配置:Babel 7.12+ 是你的底线
ES2020 特性并非所有浏览器都原生支持(尤其BigInt和?./??在旧版 Safari/IE 中缺失),因此生产环境几乎都需要 Babel 转译。Babel 7.12 是第一个完整支持 ES2020 的版本,低于此版本会有遗漏。
核心插件配置:
@babel/plugin-proposal-nullish-coalescing-operator:对应??@babel/plugin-proposal-optional-chaining:对应?.@babel/plugin-proposal-bigint:对应BigInt@babel/plugin-proposal-promise-all-settled:对应Promise.allSettled@babel/plugin-proposal-global-this:对应globalThis@babel/plugin-syntax-dynamic-import:对应import()(注意:这是 syntax 插件,不转译,仅启用解析)@babel/plugin-proposal-string-match-all:对应matchAll
.babelrc示例:
{ "presets": [ ["@babel/preset-env", { "targets": { "browsers": ["> 0.5%", "last 2 versions", "Firefox ESR", "not dead"] }, "useBuiltIns": "usage", "corejs": 3 }] ], "plugins": [ "@babel/plugin-proposal-nullish-coalescing-operator", "@babel/plugin-proposal-optional-chaining", "@babel/plugin-proposal-bigint", "@babel/plugin-proposal-promise-all-settled", "@babel/plugin-proposal-global-this", "@babel/plugin-syntax-dynamic-import", "@babel/plugin-proposal-string-match-all" ] }关键细节:
@babel/preset-env的useBuiltIns: 'usage'会根据你代码中实际使用的特性(如Promise.allSettled),自动引入core-js的 polyfill,避免全量引入。corejs: 3是必须的,因为core-js@2不支持Promise.allSettled和matchAll。
3.2 TypeScript 支持:类型安全的双保险
TypeScript 3.7+ 原生支持?.和??,并提供了强大的类型推导:
obj?.prop的类型是T | undefined(如果prop类型是T)left ?? right的类型是leftType | rightType,且会正确排除null/undefined
对于BigInt,TS 3.2+ 引入了bigint类型,但需注意:
123n的字面量类型是123n(字面量类型),不是泛型bigint- 要声明变量为
bigint,需显式标注:const num: bigint = 123n; BigInt()构造函数返回bigint,但Number()转换bigint会报错(BigInt不能隐式转Number)
TS 配置 (tsconfig.json) 关键项:
{ "compilerOptions": { "target": "ES2019", // 或更高,但 ES2020 不是必须的,Babel 会处理 "lib": ["ES2020", "DOM"], // 显式加入 ES2020,确保类型定义可用 "strict": true, "noImplicitAny": true, "esModuleInterop": true } }3.3 浏览器兼容性与 Polyfill 策略:别让BigInt在 Safari 13 上崩溃
| 特性 | Chrome | Firefox | Safari | Edge | Node.js |
|---|---|---|---|---|---|
?./?? | 80+ | 74+ | 13.1+ | 88+ | 14.0+ |
BigInt | 67+ | 68+ | 14+ | 79+ | 10.4+ |
Promise.allSettled | 76+ | 71+ | 13+ | 79+ | 12.9+ |
globalThis | 71+ | 65+ | 12.1+ | 76+ | 12.0+ |
import() | 63+ | 67+ | 11.1+ | 76+ | 12.0+ |
matchAll | 73+ | 67+ | 13+ | 79+ | 12.0+ |
Polyfill 策略:
?./??:Babel 转译即可,无需 runtime polyfill。BigInt:core-js@3提供了BigIntpolyfill,但性能极差(基于字符串模拟),仅用于开发调试。生产环境强烈建议:检测浏览器支持,不支持则降级或提示。检测代码:if (typeof BigInt === 'undefined') { alert('您的浏览器不支持大整数运算,请升级到 Safari 14+/Chrome 67+'); }Promise.allSettled:core-js@3polyfill 成熟稳定,可放心使用。globalThis:core-js@3polyfill 安全可靠。import():无需 polyfill,现代构建工具已完美支持代码分割。
3.4 生产环境避坑清单:那些让你加班到凌晨的“小细节”
?.与delete的组合是语法错误delete obj?.prop会报SyntaxError。正确做法是先判断再删除:if (obj?.prop !== undefined) { delete obj.prop; }BigInt不能与Number混合运算,但==比较是特例10n == 10返回true,但10n === 10返回false。在switch语句中,BigInt会触发default分支(因为switch使用===)。务必统一类型。import()的路径必须是字符串字面量或模板字符串,不能是变量拼接import('./' + moduleName + '.js')会报错,因为构建工具无法静态分析。必须写成import(./${moduleName}.js)(模板字符串),或使用webpackChunkName注释:const module = await import( /* webpackChunkName: "dynamic-[request]" */ `./${moduleName}.js` );matchAll()返回的是迭代器,不是数组
不能直接.map()或.forEach()。必须先转为数组:[...str.matchAll(regex)],或用for...of循环:for (const match of str.matchAll(regex)) { console.log(match); }Promise.allSettled的reason是原始错误对象,不是字符串results[0].reason.message可能是undefined(如果reason是一个普通对象)。安全访问方式:const errorMsg = results[0].reason instanceof Error ? results[0].reason.message : String(results[0].reason);
4. 真实项目问题排查实录:从报错日志到根因修复的全过程
理论再扎实,不如一次真实的线上故障排查来得深刻。以下是我在三个不同项目中,利用 ES2020 特性快速定位并解决棘手问题的真实案例,附带完整的排查思路和修复代码。
4.1 案例一:电商后台“库存同步失败”告警——?.揭开数据源污染真相
现象:
凌晨2点,监控系统报警:InventorySyncJob失败率突增至40%,错误日志为TypeError: Cannot read property 'stock' of undefined。该 Job 负责从 ERP 系统拉取 SKU 库存,更新到本地缓存。
初步排查:
- 查看最近部署记录:无相关代码变更。
- 检查 ERP 接口文档:
/api/sku/inventory返回结构未变。 - 本地模拟调用:返回数据正常,
response.data.skuList[0].stock存在。
深入分析:
日志中失败的 SKU ID 集中在几个特定品类。我登录 ERP 后台,发现这些品类的 SKU 正在进行“临时下架”操作,其库存数据被 ERP 临时设为null。但我们的代码是这样写的:
// 旧代码:假设 data.skuList[i] 永远存在 const stock = response.data.skuList[i].stock;当response.data.skuList[i]是null时,null.stock报错。
ES2020 解法:
将访问链全面升级为可选链,并结合空值合并提供默认值:
// 修复后:健壮、清晰、一行解决 const stock = response.data?.skuList?.[i]?.stock ?? 0; // 如果 skuList 是 null/undefined,或 i 超出范围,或 stock 是 null/undefined,都返回 0效果:
- 修复后,Job 失败率归零。
- 更重要的是,
?.让我们意识到:数据源本身是不可信的。这个修复不仅解决了当前 bug,还推动团队建立了“所有外部 API 响应字段访问必须用?.”的代码规范。
4.2 案例二:SaaS 平台“报表导出空白”——Promise.allSettled暴露隐藏的权限错误
现象:
用户反馈,点击“导出年度报表”按钮后,页面长时间无响应,最终弹出“导出失败”。前端 Network 面板显示,/api/reports/annual请求返回 200,但响应体为空 JSON{}。
排查过程:
- 后端日志:该接口调用三个子服务(
analytics-service,billing-service,user-service),其中billing-service返回了 403 Forbidden。 - 前端代码:使用
Promise.all并行调用三个服务:
由于const [analytics, billing, users] = await Promise.all([ fetch('/api/analytics'), fetch('/api/billing'), // 这里 403,导致 all reject fetch('/api/users') ]);Promise.all的“全或无”特性,billing的 403 导致整个Promise.allreject,analytics和users的成功响应被丢弃,前端无法组装报表数据,只能报错。
ES2020 解法:
重构为Promise.allSettled,并添加精细化错误处理:
const results = await Promise.allSettled([ fetch('/api/analytics'), fetch('/api/billing'), fetch('/api/users') ]); const [analyticsRes, billingRes, usersRes] = results; // 单独处理每个结果 let analyticsData = null; if (analyticsRes.status === 'fulfilled') { analyticsData = await analyticsRes.value.json(); } else { console.error('Analytics service failed:', analyticsRes.reason); showNotification('分析数据加载失败,部分报表可能不完整'); } let billingData = null; if (billingRes.status === 'fulfilled') { billingData = await billingRes.value.json(); } else if (billingRes.reason.status === 403) { // 特殊处理:权限不足,显示友好提示 showNotification('您没有查看账单数据的权限,报表中将不包含财务信息'); } else { console.error('Billing service failed:', billingRes.reason); } // ... 同理处理 usersData // 最终,即使 billing 失败,analytics 和 users 数据仍可用,报表至少部分生成效果:
- 用户看到的是“账单数据因权限不足未加载,其余数据已导出”,而非笼统的“导出失败”。
- 运维同学能立即从日志中看到是
billing-service的 403,而非一个模糊的Promise.allreject。
4.3 案例三:IoT 设备管理平台“设备状态异常”——BigInt精度丢失引发的连锁反应
现象:
某型号工业传感器上报的uptime(运行毫秒数)在设备运行约 25 天后,前端显示的“在线时长”开始跳变,有时显示为负数,有时显示为巨大正数(如9223372036854776000)。
根因分析:
- 传感器固件使用 64 位整数存储
uptime,通过 MQTT 发送到后端。 - 后端 Node.js 服务(v12.16)接收后,用
Number(payload.uptime)转为 JS Number。 - 当
uptime超过2^53(约 25.3 天),Number精度丢失,9007199254740991和9007199254740992在Number中表示为同一个值。 - 前端计算
currentUptime - lastUptime时,由于精度丢失,差值计算错误,导致“在线时长”计算失真。
ES2020 解法:
前后端全线升级为BigInt:
- 后端(Node.js):接收 MQTT payload 时,用
BigInt(payload.uptime)替代Number()。 - 前端:API 响应中,
uptime字段改为字符串(如"12345678901234567890"),前端用BigInt(res.uptime)解析。 - 计算逻辑:所有时间差、比较、格式化均在
BigInt上进行:const currentUptime = BigInt(res.current.uptime); const lastUptime = BigInt(res.last.uptime); const diffMs = currentUptime - lastUptime; // 精确的 BigInt 差值 const diffSecs = diffMs / 1000n; // BigInt 除法,结果仍是 BigInt const hours = diffSecs / 3600n;
效果:
- 设备运行 100 天后,前端显示的“在线时长”与设备固件内置时钟完全一致,误差为 0。
BigInt的toString()输出是精确的十进制字符串,UI 显示不再有科学计数法干扰。
5. 进阶技巧与未来演进:让 ES2020 成为你技术雷达的基准线
掌握 ES2020 不是终点,而是你构建现代 JavaScript 技术栈的起点。它教会你的,不仅是7个新语法,更是一种“防御性、精确性、渐进式”的工程哲学。以下是我总结的进阶实践和对未来趋势的观察。
5.1 组合技:?.+??+await= 一行健壮的异步数据获取
这三个特性组合起来,能写出极其简洁又健壮的异步数据流:
// 获取用户头像 URL,步骤:API 调用 → 解析 JSON → 取 avatar 字段 → 默认值 const avatarUrl = (await (await fetch('/api/user')).json())?.avatar?.url ?? '/default-avatar.png'; // 拆解理解: // 1. fetch('/api/user') → Promise<Response> // 2. await 第一个 Promise → Response // 3. .json() → Promise<any> // 4. await 第二个 Promise → any (可能是 null/undefined) // 5. ?.avatar?.url → 安全访问,可能为 undefined // 6. ?? '/default-avatar.png' → 提供默认值这行代码涵盖了网络请求、JSON 解析、空值处理、默认值设置四个环节,且每一环都具备防御性。它比写一个async function+try/catch+if判断的代码更短、更易读、更少出错。
5.2 TypeScript 与BigInt的深度协同:类型即契约
在 TS 中,BigInt的类型安全能发挥更大价值。例如,定义一个精确的“货币”类型:
type CurrencyAmount = { value: bigint; // 精确到最小单位(如分) currency: 'CNY' | 'USD' | 'EUR'; }; // 安全的加法函数 function addAmount