如何在TypeScript中实现对象键名深转换:snake_case与camelCase互转?
TypeScript 实现对象键名蛇形/驼峰深度转换
要实现带类型推导的深度转换,核心是利用TypeScript的递归泛型类型跟踪转换后的键名和结构,同时保留原函数的递归遍历逻辑。以下是完整实现:
1. 字符串转换类型定义
先定义蛇形转驼峰、驼峰转蛇形的字符串类型,用于推导转换后的键名:
// snake_case 转 camelCase type SnakeToCamel<S extends string> = S extends `${infer P}_${infer R}` ? `${P}${Capitalize<SnakeToCamel<R>>}` : S; // camelCase 转 snake_case type CamelToSnake<S extends string> = S extends `${infer P}${infer R}` ? P extends Lowercase<P> ? `${P}${CamelToSnake<R>}` : `_${Lowercase<P>}${CamelToSnake<R>}` : S;
2. 递归对象/数组类型转换
基于上面的字符串类型,定义递归处理对象和数组的泛型类型:
// 深度将对象键名从 snake_case 转为 camelCase type CamelCaseKeys<T> = T extends Array<infer U> ? Array<CamelCaseKeys<U>> : T extends object ? { [K in keyof T as SnakeToCamel<Extract<K, string>>]: CamelCaseKeys<T[K]> } : T; // 深度将对象键名从 camelCase 转为 snake_case type SnakeCaseKeys<T> = T extends Array<infer U> ? Array<SnakeCaseKeys<U>> : T extends object ? { [K in keyof T as CamelToSnake<Extract<K, string>>]: SnakeCaseKeys<T[K]> } : T;
3. 辅助工具函数
实现判断普通对象的类型守卫,以及字符串格式转换函数:
// 判断是否为普通对象(排除数组、null、内置对象等) function isPlainObject(value: unknown): value is Record<string, unknown> { return typeof value === 'object' && value !== null && Object.prototype.toString.call(value) === '[object Object]'; } // 字符串 snake_case 转 camelCase function camelCase(str: string): string { return str.replace(/_([a-z])/g, (_, char) => char.toUpperCase()); } // 字符串 camelCase 转 snake_case function snakeCase(str: string): string { return str.replace(/([A-Z])/g, '_$1').toLowerCase(); }
4. 核心转换函数
结合类型定义和工具函数,实现带类型推导的深度转换函数:
// 深度转换对象键名为 camelCase function keyToCamelCase<T>(obj: T): CamelCaseKeys<T> { if (Array.isArray(obj)) { return obj.map(item => keyToCamelCase(item)) as CamelCaseKeys<T>; } if (!isPlainObject(obj)) { return obj as CamelCaseKeys<T>; } const newObj = {} as CamelCaseKeys<T>; Object.entries(obj).forEach(([key, value]) => { const camelKey = camelCase(key) as keyof CamelCaseKeys<T>; newObj[camelKey] = keyToCamelCase(value); }); return newObj; } // 深度转换对象键名为 snake_case function keyToSnakeCase<T>(obj: T): SnakeCaseKeys<T> { if (Array.isArray(obj)) { return obj.map(item => keyToSnakeCase(item)) as SnakeCaseKeys<T>; } if (!isPlainObject(obj)) { return obj as SnakeCaseKeys<T>; } const newObj = {} as SnakeCaseKeys<T>; Object.entries(obj).forEach(([key, value]) => { const snakeKey = snakeCase(key) as keyof SnakeCaseKeys<T>; newObj[snakeKey] = keyToSnakeCase(value); }); return newObj; }
示例使用
// 定义蛇形键的对象类型 interface SnakeCaseUser { user_name: string; user_age: number; user_address: { street_name: string; zip_code: string; }; favorite_colors: string[]; } // 蛇形键对象实例 const snakeUser: SnakeCaseUser = { user_name: 'Alice', user_age: 28, user_address: { street_name: 'Oak Ave', zip_code: '67890' }, favorite_colors: ['purple', 'yellow'] }; // 转换为驼峰键对象,类型自动推导 const camelUser = keyToCamelCase(snakeUser); // camelUser 的类型为: // { // userName: string; // userAge: number; // userAddress: { // streetName: string; // zipCode: string; // }; // favoriteColors: string[]; // } // 转换回蛇形键对象,类型匹配原定义 const convertedBack = keyToSnakeCase(camelUser); // convertedBack 类型为 SnakeCaseUser
内容的提问来源于stack exchange,提问作者n8o
相关产品推荐
相关产品推荐

