news 2026/9/17 6:22:41

es-toolkit 的 toCamelCaseKeys 详解:递归将对象与数组键转换为 camelCase

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
es-toolkit 的 toCamelCaseKeys 详解:递归将对象与数组键转换为 camelCase

es-toolkit 的 toCamelCaseKeys 详解:递归将对象与数组键转换为 camelCase

【免费下载链接】es-toolkitA modern JavaScript utility library that's 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit

toCamelCaseKeys是 es-toolkit 提供的对象工具函数,它将对象、嵌套对象以及数组中的对象的所有键递归转换为 camelCase(小驼峰)命名,并返回一个全新的对象。它尤其适合处理来自后端 API 的snake_casePascalCase或全大写命名的数据,让你在接入前端代码时无需手工逐层改写字段名。读完本文,你将掌握它的转换规则、递归边界、类型推导能力以及源码级实现原理。

camelCase 命名规则

camelCase(小驼峰)是一种命名约定:标识符的第一个单词全部小写,后续每个单词的首字母大写并直接拼接,中间不使用任何分隔符。例如user_id转为userIdFIRST_NAME转为firstName

const camelCased = toCamelCaseKeys(obj);

使用方式

toCamelCaseKeys(obj)

当你需要把对象的所有键转换为 camelCase 时,直接调用toCamelCaseKeys即可。嵌套对象、数组内的对象都会被递归转换。

键的转换遵循以下规则:

  • snake_casecamelCase(例如user_iduserId
  • PascalCasecamelCase(例如UserIduserId
  • 全大写键 →camelCase(例如FIRST_NAMEfirstNameLASTlast
import { toCamelCaseKeys } from 'es-toolkit/object'; // 基本对象转换 const obj = { user_id: 1, first_name: 'John', last_name: 'Doe' }; const result = toCamelCaseKeys(obj); // result 为 { userId: 1, firstName: 'John', lastName: 'Doe' } // 数组内的对象也会被转换 const users = [ { user_id: 1, first_name: 'John' }, { user_id: 2, first_name: 'Jane' }, ]; const convertedUsers = toCamelCaseKeys(users); // convertedUsers 为 [{ userId: 1, firstName: 'John' }, { userId: 2, firstName: 'Jane' }] // 嵌套对象被完整转换 const nested = { user_data: { user_id: 1, contact_info: { email_address: 'john@example.com', phone_number: '123-456-7890', }, }, }; const nestedResult = toCamelCaseKeys(nested); // nestedResult 为 { // userData: { // userId: 1, // contactInfo: { // emailAddress: 'john@example.com', // phoneNumber: '123-456-7890' // } // } // } // PascalCase 与全大写键也会被转换 const raw = { UserId: 1, FIRST_NAME: 'JinHo', LAST: 'Yeom' }; const converted = toCamelCaseKeys(raw); // converted 为 { userId: 1, firstName: 'JinHo', last: 'Yeom' }
参数
  • objT):需要将键转换为 camelCase 的对象、数组或原始值。
返回值

ToCamelCaseKeys<T>):返回所有键均转换为 camelCase 的新对象。注意返回值是新对象,原对象不会被修改。

源码实现:递归与分支处理

从源码结构看,toCamelCaseKeys的实现位于 src/object/toCamelCaseKeys.ts,整体逻辑是对三种输入分别处理:

export function toCamelCaseKeys<T>(obj: T): ToCamelCaseKeys<T> { if (isArray(obj)) { return obj.map(item => toCamelCaseKeys(item)) as ToCamelCaseKeys<T>; } if (isPlainObject(obj)) { const result = {} as ToCamelCaseKeys<T>; const keys = Object.keys(obj); for (let i = 0; i < keys.length; i++) { const key = keys[i]; const camelKey = camelCase(key) as keyof typeof result; const convertedValue = toCamelCaseKeys(obj[key]); result[camelKey] = convertedValue as ToCamelCaseKeys<T>[keyof ToCamelCaseKeys<T>]; } return result; } return obj as ToCamelCaseKeys<T>; }

实现要点如下:

  1. 数组分支:通过isArray判断后,用map对每个元素递归调用toCamelCaseKeys,因此数组中的对象及其嵌套结构都会被转换;
  2. 普通对象分支:通过isPlainObject判断(导入自 src/predicate/isPlainObject.ts),用Object.keys遍历自有可枚举键,将每个键交给camelCase转换后写入新对象,同时对该键的值递归转换;
  3. 原始值兜底:非数组、非普通对象的值(数字、字符串、布尔值、nullundefined等)原样返回,不做任何处理。

键转换的底层:camelCase 与单词切分

键名转换最终由 src/string/camelCase.ts 完成。它先用words函数按 src/string/words.ts 中的CASE_SPLIT_PATTERN正则将字符串切分为单词数组,再取第一个单词小写、后续单词首字母大写拼接:

export function camelCase(str: string): string { const words = getWords(str); if (words.length === 0) { return ''; } const [first, ...rest] = words; return `${first.toLowerCase()}${rest.map(word => capitalize(word)).join('')}`; }

CASE_SPLIT_PATTERN基于 Unicode 属性(\p{Lu}\p{Ll}\p{Emoji_Presentation}等)匹配单词,这意味着支持 unicode 字符与 emoji。例如camelCase('HTTPRequest')会得到httpRequestcamelCase('Keep unicode 😅')会得到keepUnicode😅。这也是FIRST_NAME这种全大写键能正确切成FIRSTNAME两个词的原因。

类型层面:ToCamelCaseKeys 的递归类型推导

toCamelCaseKeys的返回值类型是ToCamelCaseKeys<T>,定义于 src/types/ToCamelCaseKeys.ts。它是一组递归的条件类型,与运行时行为严格对齐:

type SnakeToCamel<S extends string> = S extends `${infer H}_${infer T}` ? `${Lowercase<H>}${Capitalize<SnakeToCamel<T>>}` : Lowercase<S>; type PascalToCamel<S extends string> = S extends `${infer F}${infer R}` ? `${Lowercase<F>}${R}` : S; type AnyToCamel<S extends string> = S extends `${string}_${string}` ? SnakeToCamel<S> : S extends Uppercase<S> ? Lowercase<S> : PascalToCamel<S>; export type ToCamelCaseKeys<T> = T extends NonPlainObject ? T : T extends any[] ? Array<ToCamelCaseKeys<T[number]>> : T extends Record<string, any> ? { [K in keyof T as AnyToCamel<Extract<K, string>>]: ToCamelCaseKeys<T[K]> } : T;

类型层的三条规则与运行时分支一一对应:

  • AnyToCamel用模板字面量类型处理三种键:含下划线的按SnakeToCamel递归转换;全大写的整体Lowercase;其余(含 PascalCase)仅将首字母小写;
  • 映射类型{ [K in keyof T as AnyToCamel<...>]: ... }通过as重映射键名,同时递归转换值类型,因此嵌套对象和数组在编译期就能得到精确的类型;
  • NonPlainObject(定义于 src/_internal/NonPlainObject.ts)列出了DateRegExpMapSetPromiseErrorArrayBuffer、各类 TypedArray、函数以及globalThis等内置对象类型,这些类型直接原样通过,不做键转换,避免对内置对象的内部结构做无意义且危险的映射。
type Response = { user_id: number; first_name: string }; type Converted = ToCamelCaseKeys<Response>; // => { userId: number; firstName: string }

该类型在 docs/types/reference/objects/ToCamelCaseKeys.md 中有独立文档说明。

边界行为与测试验证

src/object/toCamelCaseKeys.spec.ts 用 Vitest 覆盖了完整的行为边界,可作为实际使用时的参考:

  • 数组内对象与对象内数组双向递归{ user_list: [...] }中数组元素的键同样被转换;
  • 原始值原样返回123'string'nullundefinedtrue传入后均原样返回;
  • 空对象与空数组{}[]分别返回{}[]
  • 保留原型方法:值为Object.prototype.toString这类函数属性的键不会被破坏;
  • 不递归非普通对象DateMapSetRegExp等值只作为普通值保留,created_at: date只把键转为createdAt,值仍是同一个Date实例;
  • 类型级验证:测试通过expectTypeOf断言嵌套对象、数组、混合结构与全大写键(如as constFIRST_NAME)在编译期都得到正确的 camelCase 类型。

与 lodash/fp 的对比基准

仓库在 benchmarks/performance/toCamelCaseKeys.bench.ts 中提供了针对深层嵌套对象的性能对比基准,与lodash/fpmapKeys(camelCase)组合在同一结构上对比(lodash 方案为浅层转换)。基准数据使用 Vitest 的bench运行,你可以在仓库中查看该文件了解对比设置与数据结构。需要注意的是,lodash/fp 方案默认只转换顶层键,而 es-toolkit 的toCamelCaseKeys是深度递归转换,二者行为并不完全等价。

使用建议

  • toCamelCaseKeys从 src/object/index.ts 导出,可通过import { toCamelCaseKeys } from 'es-toolkit/object'按需引入,也可从es-toolkit主入口导入;
  • 它返回新对象,适合用于 API 响应数据的清洗层,例如在请求封装里统一对响应体做toCamelCaseKeys转换,再交给业务代码使用;
  • 由于转换是深度递归的,对于超大对象会有一定的遍历开销,可结合实际数据规模评估是否需要在边缘层缓存结果;
  • 若需要反向转换(camelCase → snake_case),仓库还提供了对应的toSnakeCaseKeys,与本文的转换规则互为镜像,可配合使用。

【免费下载链接】es-toolkitA modern JavaScript utility library that's 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 6:19:51

Python+Django构建高效药房管理系统实战

1. 项目概述与背景药房作为医疗行业的重要环节&#xff0c;其管理复杂度高、需求变化快的特点一直困扰着从业者。传统的人工管理方式不仅效率低下&#xff0c;还容易出现信息不准确、操作繁琐等问题。我在实际工作中发现&#xff0c;一个中型药房每天需要处理上百种药品的出入库…

作者头像 李华
网站建设 2026/9/17 6:19:39

深入理解ARM电源架构:从PPU到电源域的完整管理链路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 6:19:37

CubeSandbox版本升级指南:从0.3到0.6平滑升级避坑清单

CubeSandbox版本升级指南&#xff1a;从0.3到0.6平滑升级避坑清单 【免费下载链接】CubeSandbox Instant, Concurrent, Secure & Lightweight Sandbox for AI Agents. 项目地址: https://gitcode.com/GitHub_Trending/cu/CubeSandbox CubeSandbox 是面向 AI Agent 的…

作者头像 李华
网站建设 2026/9/17 6:18:42

5款高效办公软件推荐:超越WPS与Office的局限

1. 办公软件新选择&#xff1a;打破WPS与Office的思维定式在数字化办公领域&#xff0c;大多数人的认知长期被WPS和微软Office二分法所局限。作为一名在IT行业深耕十余年的技术顾问&#xff0c;我见证了太多企业因工具选择单一而错失效率提升的机会。今天要介绍的这5款办公软件…

作者头像 李华
网站建设 2026/9/17 6:18:34

需求分析与技术选型:从模糊意图到可执行决策的实战手记

1. 这不是一篇“开篇”&#xff0c;而是一份被反复验证过的技术决策手记“01 开篇&#xff1a;需求分析与技术选型”——看到这个标题&#xff0c;别急着划走。它表面像教程目录里的一个占位符&#xff0c;实则藏着整个项目成败的伏笔。我做过17个从零启动的交付型项目&#x…

作者头像 李华