实战小技巧:下划线转驼峰篇

前言

在日常的开发需求中,经常会碰到协作方的变量命名风格与我们自己项目中的变量命名风格不一致的情况。例如后端返回的变量命名是下划线,而前端使用的变量命名是驼峰。本文介绍了一种实用的方法将对象中的变量名由下划线转为驼峰,包括Typescript类型定义的转换,如果你有兴趣的话简单花5分钟学习一下吧~

对象命名转换

字符串转换

可以使用lodash的工具函数进行下划线/驼峰字符串之间的互转:

perl 复制代码
_.snakeCase('Foo Bar');
// => 'foo_bar'
 
_.snakeCase('fooBar');
// => 'foo_bar'
 
_.snakeCase('--FOO-BAR--');
// => 'foo_bar'

_.camelCase('Foo Bar');
// => 'fooBar'
 
_.camelCase('--foo-bar--');
// => 'fooBar'
 
_.camelCase('__FOO_BAR__');
// => 'fooBar'

工具函数🔧

typescript 复制代码
function transformObjectKeys<T extends Record<string, any>, K>(
  input: T,
  keyTransformer: (key: string) => string
): K {
  // 确保输入是对象
  if (input && typeof input === 'object' && !Array.isArray(input)) {
    const result: Record<string, any> = {};

    for (const key of Object.keys(input)) {
      const transformedKey = keyTransformer(key); // 使用转换函数生成新键
      const value = input[key];

      // 递归处理嵌套对象
      if (value && typeof value === 'object' && !Array.isArray(value)) {
        result[transformedKey] = transformObjectKeys(value, keyTransformer);
      } else {
        result[transformedKey] = value;
      }
    }

    return result as K;
  }

  // 如果不是对象,直接返回原始输入
  return input as unknown as K;
}


function formatApiResponse<T, K>(
  response: T,
  keyTransformer: (key?: string) => string = camelCase,
  deepClone = true
): K {
  const input = deepClone ? cloneDeep(response) : response;
  return transformObjectKeys(input, keyTransformer);
}

function formatApiRequest<T, K>(
  requestData: T,
  keyTransformer: (key?: string) => string = snakeCase,
  deepClone = false
): K {
  const input = deepClone ? cloneDeep(requestData) : requestData;
  return transformObjectKeys(input, keyTransformer) as K;
}

使用示例:

typescript 复制代码
const formattedResponse = formatApiResponse(apiResponse);
const apiResponse = { first_name: "John"};

// 调用 formatApiResponse 
const formattedResponse = formatApiResponse(apiResponse); console.log(formattedResponse);
// { firstName: "John"}

类型命名转换

除了可以将对象的成员变量命名进行下划线/驼峰互转之外,typescript类型也可以进行转换。 关于类型体操的前置学习文档,有兴趣可以看一下:

www.typescriptlang.org/docs/handbo... www.typescriptlang.org/docs/handbo...

下划线转驼峰

typescript 复制代码
// 将 Snake_Case 转换为 PascalCase
type SnakeToPascalCase<T extends string> = 
  T extends `${infer Head}_${infer Tail}`
    ? `${Capitalize<Head>}${SnakeToPascalCase<Tail>}`
    : Capitalize<T>;

// 将 Snake_Case 转换为 camelCase
type SnakeToCamelCase<T extends string> = 
  T extends `${infer Head}_${infer Tail}`
    ? `${Lowercase<Head>}${SnakeToPascalCase<Tail>}`
    : T;
    
// 将对象中的键从 Snake_Case 转为 camelCase,并递归处理子对象或数组
type SnakeToCamelCasedData<T> = T extends Array<infer U>
  ? SnakeToCamelCasedData<U>[]
  : T extends object
  ? {
      [K in keyof T as SnakeToCamelCase<Extract<K, string>>]: SnakeToCamelCasedData<T[K]>;
    }
  : T;

使用示例:

typescript 复制代码
interface SnakeCaseData {
  user_name: string;
  user_age: number;
}

// 将类型转换为 camelCase
type CamelCasedData = SnakeToCamelCasedData<SnakeCaseData>;

// 转换后的类型
type CamelCasedData = {
  userName: string;
  userAge: number;
};

驼峰转下划线

typescript 复制代码
// 将 camelCase 转换为 snake_case
type CamelToSnakeCase<T extends string> = T extends `${infer Char}${infer Rest}`
  ? `${Char extends Uppercase<Char> ? `_${Lowercase<Char>}` : Char}${CamelToSnakeCase<Rest>}`
  : T;

// 将对象中的键从 camelCase 转换为 snake_case,并递归处理子对象或数组
export type CamelToSnakeCasedData<T> = 
  T extends (...args: any[]) => any // 如果是函数类型,直接返回原类型
    ? T
    : T extends Array<infer U> // 如果是数组,递归处理每个元素
    ? CamelToSnakeCasedData<U>[]
    : T extends object // 如果是对象,递归处理每个键和值
    ? {
        [K in keyof T as CamelToSnakeCase<Extract<K, string>>]: CamelToSnakeCasedData<T[K]>;
      }
    : T; // 如果是基础类型,直接返回原类型

使用示例:

typescript 复制代码
interface CamelCaseData {
  userName: string;
  userAge: number;
}

// 将类型转换为 snakeCase
type SnakeCasedData = CamelToSnakeCasedData<CamelCaseData>;

// 转换后的类型
type SnakeCasedData = {
  user_name: string;
  user_age: number;
};
相关推荐
大圣编程30 分钟前
Python中continue语句的用法是什么?
开发语言·前端·python
yuhaiqiang31 分钟前
随手 vibecoding 的浏览器插件已经 6000 多次下载,聊聊他的产品设计
前端·后端·面试
之歆1 小时前
Vue商品详情与放大镜组件
前端·javascript·vue.js
再吃一根胡萝卜2 小时前
如何把小米 MiMo 接入 CodeBuddy,打造私有 Agent
前端
负责的蛋挞3 小时前
异步HttpModule的实现方式
java·服务器·前端
丹宇码农6 小时前
把 HLS 字幕玩出花:zwPlayer 如何让 M3U8 视频支持全文搜索、翻译与码率自适应
前端·javascript·音视频·hls·视频播放器
2501_943782356 小时前
【共创季稿事节】猜数字游戏:二分法思维与交互式反馈
前端·游戏·microsoft·harmonyos·鸿蒙·鸿蒙系统
GV191rLvq6 小时前
基于Socket实现的最简单的Web服务器【ASP.NET原理分析】
服务器·前端·asp.net
吠品6 小时前
LangChain 里 tool_call_id 为空?一次 MCP 工具集成的排查记录
前端