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")[],达成了预期的优化效果。

相关推荐
亭台烟雨中10 分钟前
【前端记事】关于electron的入门使用
前端·javascript·electron
泯泷24 分钟前
「译」解析 JavaScript 中的循环依赖
前端·javascript·架构
Senar1 小时前
Web端选择本地文件的几种方式
前端·javascript·html
烛阴1 小时前
JavaScript 的 8 大“阴间陷阱”,你绝对踩过!99% 程序员崩溃瞬间
前端·javascript·面试
lh_12542 小时前
ECharts 地图开发入门
前端·javascript·echarts
周之鸥3 小时前
使用 Electron 打包可执行文件和资源:完整实战教程
前端·javascript·electron
前端snow3 小时前
前端全栈第二课:用typeorm向数据库添加数据---一对多关系
前端·javascript
全栈老李技术面试3 小时前
【高频考点精讲】async/await原理剖析:Generator和Promise的完美结合
前端·javascript·css·vue·html·react·面试题