TypeScript 将通用对象从蛇形转换为驼色盒

mat*_*ich 12 typescript

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

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

function snakeToCamelCase(object: T): U {
  ...
}
Run Code Online (Sandbox Code Playgroud)

键入TU.

我想U尽可能窄地键入,并且理想地U基于的类型T

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

{
  snakeCaseKey1: number,
  snakeCaseKey2: string,
  ...
}
Run Code Online (Sandbox Code Playgroud)

for*_*d04 23

解决方案

操场

这在 TypeScript 4.1 中的模板文字类型中是可能的(另请参见snake_case):

type SnakeToCamelCase<S extends string> =
  S extends `${infer T}_${infer U}` ?
  `${T}${Capitalize<SnakeToCamelCase<U>>}` :
  S
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)

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

type OutputType = {[K in keyof InputType as SnakeToCamelCase<K>]: InputType[K]}
/* 
  type OutputType = {
      snakeCaseKey1: number;
      snakeCaseKey2: string;
  }
*/
Run Code Online (Sandbox Code Playgroud)

扩展

反转类型

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"
Run Code Online (Sandbox Code Playgroud)

Pascal case、Kebab case 和倒置

一旦您获得了上述类型,就可以很容易地通过使用内部字符串类型 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>>
Run Code Online (Sandbox Code Playgroud)

对于烤肉串盒,用 替换_蛇盒类型-

转换嵌套属性

type SnakeToCamelCaseNested<T> = T extends object ? {
  [K in keyof T as SnakeToCamelCase<K & string>]: SnakeToCamelCaseNested<T[K]>
} : T
Run Code Online (Sandbox Code Playgroud)

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

很长的字符串可能会发生此错误。您可以一次性处理多个子项,以将类型递归限制在编译器可接受的范围内。例如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
Run Code Online (Sandbox Code Playgroud)

注:在第一种情况,TU每一个推断子项,而V该字符串的推断休息。

  • 这不是要把 Array 原型中的属性驼峰化吗?(如推入、弹出等..) (3认同)

Jul*_*era 15

FWIW,我遇到了@ford04 答案中类型的一些问题。我发现CamelCasedPropertieshttps://github.com/sindresorhus/type-festSnakeCasedProperties到目前为止工作良好。

  • 我喜欢 @ford04 在他的回答中分享的努力,但我会选择你分享的库! (3认同)

eld*_*apo -2

不幸的是,这样的事情是不可能的。当前形式的 Typescript 不支持类型键转换/映射。

  • 没有什么可以阻止您手动迭代对象的属性、操作它们的键以及以操作的形式将它们写入另一个对象。然而,这不是问题的所在——它询问的是如何输入这样一个函数,而不是它的实现。 (2认同)