Object.keys 的原生 JS 类型之困

前言

在 TS 项目开发时,大家想必都遭遇过一个颇为头疼的状况:原生 JS 的一些方法,类型定义并不完善。日常编码中,这就像个小 "绊脚石",你本想高效推进项目,却常常因为它,不得不暂停下来,写一堆额外代码去弥补类型缺失带来的隐患,无端耗费许多精力,别提多闹心了。

被被这问题纠缠久了,我实在忍无可忍。与其每次都临时抱佛脚应付,不如主动出击,彻查根源,找个能彻底改善局面的法子。

下面,我打算以 Object.keys 为典型案例,进行深入剖析,旨在为面临同样困境的开发者提供有益的参考与启发。

问题描述

深入探究 Object.keys 原始的类型定义,可归纳出两个关键特征:

通过上面代码我们可以看到两个点

  1. 该方法隶属于 ObjectConstructor 范畴。
  2. 其参数类型仅被简单定义为 object,返回值类型为 string[],且不支持泛型这一特性,致使其类型表达能力受限。

如此的类型设定,在实际编码中极易引发问题。参考以下示例代码

typescript 复制代码
const obj = { aaa: "0001", bbb: "0002" };

Object.keys(obj).forEach((key) => {
  obj[key] // 类型报错
  ~~~~~~~~
  // 元素隐式具有 "any" 类型,因为类型为 "string" 的表达式不能用于索引类型 "{ aaa: string; bbb: string; }"。
  // 在类型 "{ aaa: string; bbb: string; }" 上找不到具有类型为 "string" 的参数的索引签名。
});

由上述代码可见,当使用 Object.keys 提取对象的可枚举属性名,并尝试基于这些属性名迭代获取对应属性值时,类型错误便随之产生。

面对此类问题,部分开发者可能会采取临时的类型断言措施,如 (Object.keys(obj) as (keyof typeof obj)[]).forEach...。然而,这种方式仅能解一时之急,无法满足代码准确性与简洁性的长远需求。开发者真正期望的是,Object.keys 能够智能、精准地依据对象实例推导其包含的属性信息,而非仅仅返回一个宽泛的 string[]。

基于对该问题的深度困扰,探索如何重塑 Object.keys 的类型,以实现精准的类型推导,成为解决问题的关键思路。经研究发现,利用全局类型声明文件覆盖原有的类型定义,是一条可行的解决路径。

解决方案

  1. 首先,创建一个以 .d.ts 结尾的文件,此类文件作为类型声明文件,在 TS 项目的类型管理体系中发挥着关键作用。
  2. 鉴于前文对 Object.keys 所属范畴的分析,需在类型声明文件中对 ObjectConstructor 进行针对性处理。具体操作如下:
typescript 复制代码
// window.d.ts

interface ObjectConstructor {
  keys<T>(o: T): (keyof T)[]; // 覆盖 Object.keys 的类型
}

值得一提的是,通过 interface 在类型声明文件中声明的类型,具备全局可用性,能够在整个项目代码范围内有效约束类型,保障代码的类型安全性。

经上述改造后,再次使用 Object.keys 时,令人欣喜的是,其已能够自动推导出精准的返回值类型,如 ("aaa" | "bbb")[],达成了预期的优化效果。

相关推荐
程序员Sunday8 小时前
JavaScript 事件循环面试题,宏任务与微任务怎么执行|Sunday面试指南
开发语言·javascript·面试·校招·事件循环·程序员sunday
默_笙10 小时前
🌊 向量库和 ES 都查不出"关系",我只好给奶茶建了一张人脉网
前端·javascript
思无邪6611 小时前
用 AI 做 JS 逆向:从抓包到复现的完整方法论
开发语言·javascript·人工智能
福兮说12 小时前
浏览器里把 iPhone 的 HEIC 转成 JPG:Chrome 解不开、转完大了七成、拍摄时间全丢,六个坑实测
前端·javascript·图像处理·chrome·ios·iphone·heic
可乐鸡翅yeah_13 小时前
video.js 集成 hls.js 开发 M3U8 播放器,新手高频踩坑
开发语言·前端·javascript·后端·ecmascript·m3u8·音视频在线播放
wflynn14 小时前
GitHub 今日推荐|ts-rust:把微软 TypeScript-Go 编译器逐行移植成 Rust 实现
rust·typescript·开源·github·compiler·tsc
用户1179536547815 小时前
TRAE vs Claude Code 性能对比:从代码生成、多文件重构到底层吞吐,谁更适合你的日常开发?
javascript
kill52216 小时前
useRef
前端·javascript·react.js
米多科技16 小时前
我做了一个 3D 虚拟世界基底,在上面再搭 3D 世界或元宇宙,事半功倍
javascript·人工智能·github
霍营_托尼16 小时前
纯前端播 40GB 本地视频?我把浏览器改造成了「磁盘流式」播放器
前端·javascript·音视频开发