首页 > 解决方案 > TypeScript 将通用对象从蛇形转换为驼峰形

问题描述

我想编写一个函数,它接受带有蛇形键的对象并将其转换为带有驼峰键的对象。假设我们知道输入对象的类型,但希望解决方案是通用的,那么在 TypeScript 中输入此类函数的最佳方式是什么。

type InputType = {
  snake_case_key_1: number,
  snake_case_key_2: string,
  ...
}

function snakeToCamelCase(object: T): U {
  ...
}

可以做的最好的工作是T什么U

我想U尽可能地缩小类型,并且理想U地基于类型T

理想情况下,如果T是我的示例,InputType我希望将 U 键入为

{
  snakeCaseKey1: number,
  snakeCaseKey2: string,
  ...
}

标签: typescript

解决方案


解决方案

操场

TypeScript 4.1 中的模板文字类型可以做到这一点(另见蛇案例):

type SnakeToCamelCase<S extends string> =
  S extends `${infer T}_${infer U}` ?
  `${T}${Capitalize<SnakeToCamelCase<U>>}` :
  S
type T11 = SnakeToCamelCase<"hello"> // "hello"
type T12 = SnakeToCamelCase<"hello_world"> // "helloWorld"
type T13 = SnakeToCamelCase<"hello_ts_world"> // "helloTsWorld"
type T14 = SnakeToCamelCase<"hello_world" | "foo_bar">// "helloWorld" | "fooBar"
type T15 = SnakeToCamelCase<string> // string
type T16 = SnakeToCamelCase<`the_answer_is_${N}`>//"theAnswerIs42" (type N = 42)

然后,您将能够使用映射类型中的键重新映射来构造新的记录类型:

type OutputType = {[K in keyof InputType as SnakeToCamelCase<K>]: InputType[K]}
/* 
  type OutputType = {
      snakeCaseKey1: number;
      snakeCaseKey2: string;
  }
*/

扩展

反转类型

type CamelToSnakeCase<S extends string> =
  S extends `${infer T}${infer U}` ?
  `${T extends Capitalize<T> ? "_" : ""}${Lowercase<T>}${CamelToSnakeCase<U>}` :
  S

type T21 = CamelToSnakeCase<"hello"> // "hello"
type T22 = CamelToSnakeCase<"helloWorld"> // "hello_world"
type T23 = CamelToSnakeCase<"helloTsWorld"> // "hello_ts_world"

Pascal 案例、Kebab 案例和倒置

一旦你获得了上述类型,使用内部字符串类型 Capitalize和在它们和其他情况之间进行转换非常简单Uncapitalize

type CamelToPascalCase<S extends string> = Capitalize<S>
type PascalToCamelCase<S extends string> = Uncapitalize<S>
type PascalToSnakeCase<S extends string> = CamelToSnakeCase<Uncapitalize<S>>
type SnakeToPascalCase<S extends string> = Capitalize<SnakeToCamelCase<S>>

对于烤肉串盒,将_蛇盒类型替换为-.

转换嵌套属性

type SnakeToCamelCaseNested<T> = T extends object ? {
  [K in keyof T as SnakeToCamelCase<K & string>]: SnakeToCamelCaseNested<T[K]>
} : T

“类型实例化太深了,可能是无限的。”

很长的字符串可能会发生此错误。您可以一次性处理多个子项,以将类型递归限制在编译器可接受的范围内。例如SnakeToCamelCaseXXL

操场

type SnakeToCamelCaseXXL<S extends string> =
  S extends `${infer T}_${infer U}_${infer V}` ?
  `${T}${Capitalize<U>}${Capitalize<SnakeToCamelCaseXXL<V>>}` :
  S extends `${infer T}_${infer U}` ?
  `${T}${Capitalize<SnakeToCamelCaseXXL<U>>}` :
  S

注意:在第一个条件中,T每个U都推断一个子项,而V推断字符串的其余部分。

更新:TS 4.5 将类型实例化深度限制从 50 提高到 100,所以这个编译器技巧对于新版本是不必要的。对于更复杂的情况,您现在还可以使用尾递归评估


推荐阅读