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

前言

在日常的开发需求中,经常会碰到协作方的变量命名风格与我们自己项目中的变量命名风格不一致的情况。例如后端返回的变量命名是下划线,而前端使用的变量命名是驼峰。本文介绍了一种实用的方法将对象中的变量名由下划线转为驼峰,包括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;
};
相关推荐
持久的棒棒君3 小时前
npm安装electron下载太慢,导致报错
前端·electron·npm
crary,记忆5 小时前
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
前端·webpack·angular·angular.js
漂流瓶jz5 小时前
让数据"流动"起来!Node.js实现流式渲染/流式传输与背后的HTTP原理
前端·javascript·node.js
SamHou05 小时前
手把手 CSS 盒子模型——从零开始的奶奶级 Web 开发教程2
前端·css·web
我不吃饼干6 小时前
从 Vue3 源码中了解你所不知道的 never
前端·typescript
开航母的李大6 小时前
【中间件】Web服务、消息队列、缓存与微服务治理:Nginx、Kafka、Redis、Nacos 详解
前端·redis·nginx·缓存·微服务·kafka
Bruk.Liu6 小时前
《Minio 分片上传实现(基于Spring Boot)》
前端·spring boot·minio
鱼樱前端6 小时前
Vue3+d3-cloud+d3-scale+d3-scale-chromatic实现词云组件
前端·javascript·vue.js
zhangxingchao7 小时前
Flutter入门:Flutter开发必备Dart基础
前端
佚名猫7 小时前
vue3+vite+pnpm项目 使用monaco-editor常见问题
前端·vue3·vite·monacoeditor