TypeScript 将通用对象从蛇形命名法转换为驼峰命名法

2024-04-26

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

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

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

打字能做的最好的工作是什么T and U.

我想U尽可能缩小类型,并且类型U以基于T理想情况下。

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

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

Solution

操场 https://www.typescriptlang.org/play?#code/C4TwDgpgBAygdgQwNYQCoHsDCCC2EA22AzhADwxQQAewEcAJkVEcAE4CWcA5gHxQC8AKCixKNOoygADACQBvTgDMIrKKgC+AfXlKVUAKrqpUAPzDp8jfOxh2wBPnYAvMvGRosuAsTL6ePIygALnMYQUFQSChsPHwMNxQfcjFaBiYWDm4+IREKalTJWQU4ZVUrYtKDQLMRItQUiSYbOwdnMlQ+EygAIk1u4J7u9XkAGXQAdxUAYwQSUg7huRiCeMRE2d8A4xDc8MjoZbj0AAVZmcIN5PzG5jZOXgFohFt7RxdyHgjwaFOic4xDkk8uI0rdMg9+AY4DMXq13jBPvsoL9-ugEhAgQ1QRl7tknrFVu4kvpoc8Wm9XP4vlF0RgUQ5MddsXcso9mq82uQ1h5AZcEZ9qdBUABGYWPWmeWJJboACwI+HQ3T4AHplT05fgFd1BWphQAmcXcgFeC5zWXy9CacboVj4ehKqCq9UWgDqNrt2qRIoAzIb3MapZdzZrLcAiFb3faVWrgwrUEQ3bb7TqRQAWP0oAPeIMahURpP9AA+PUU6EtACMEKwHU7Y+hEx6oMXuqX0AAhKue766gCsGZ5JqSONZTuHXB1ADlHqm9SnhQA2ftZ01kKTAOWaBBwIiTViadjh+QTozRnrriAAQW3u4AkkQZ-0ABRIqeQmcASj23dQerFkMOhLrGauaKqedZdlEP4Gv+JqARiOaupGNYxiB+Yeimeq+jBBJoty0ogfGDZRo6KEWpoYZocmKben++IrCcZwMghIZKtR0F0Uc9IrqQdZEaxXrelhHF0ox3F1oRSECl6qa0RKXH4Ra-HfjOS4MX8THAWR1oFoiylCXJokKSG5HhtpHpSd+Pa0Vxy7SgAEopumQT27E2ZK2Zmg5IZ8U5Qo9kJbm8p5FoSTpX6QfO1miXB9mOSm86udFuFEkGXkKj58UBUl6KxSGoXmeF0A3nAYAAK7AKg3aQnI5hENymgzCQmgoCAmjCkEUBwKVODlioAA0tX1Y1EDNRArV6h1Y4DeohVQAA8uVZUVVVUByFAADaADSUCcFALXoIoUDFUtlVRLMsBGu53GbTwAC6HXHeVp0QFtt1QDNyoAFTmEiC3ACdK01SIwPMHhGybWN7Wdd1vWsAA3OYIN1SlJAQyAE1gvcCMiDNn3KuEX3fVAn1QMIJMAKIgkQ7DoHAHWYLTABuKjAJ1EAsBA9BQGArDoJArDAOw7Nk6TxP40ij3LZAE7s7QXPVYN7gNRso2tVDQPA8jKDK01LVtVDY6I6DSvDarbUY4bOMDSIWsjabesWyyXDTbNEpBRAMsc-Q8x4vUTKSOg5YAFYQFMrNdBrW07XAe1jQdahQOdbuDpc20AGSY1k90Xf6V0+J7cvzK9nzqAMqCzX9APS7LnOqe7Bec6QkvPQ39A8EAA

这是可能的模板文字类型 https://devblogs.microsoft.com/typescript/announcing-typescript-4-1/#template-literal-types在 TypeScript 4.1 中(另请参阅蛇箱 https://en.wikipedia.org/wiki/Snake_case):

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)

然后您将能够使用映射类型中的键重新映射 https://devblogs.microsoft.com/typescript/announcing-typescript-4-1/#key-remapping-mapped-types构造一个新的记录类型:

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 案例和反转

一旦获得上述类型,通过使用在它们和其他情况之间进行转换就非常简单内在字符串类型 https://github.com/microsoft/TypeScript/pull/40580 Capitalize and 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:

操场 https://www.typescriptlang.org/play?#code/C4TwDgpgBAygdgQwNYQCoHsDCCC2EA22AzhADwxQQAewEcAJkVEcAE4CWcA5gHxQC8AKCixKNOoygADACQBvTgDMIrKKgC%20AfXlKVUAKrqpUAPzDp8gDLoA7ioDGCEqVQ9187GHbAE%20dgC8yeGQ0LFwCYjJ9HjdjAC5zGEFBUEhYRBQMbDxCJwgADXzLcjFaBiYWDm4%20IREKajLJWQU4ZVUNbRa2gy0dVr0ANSNTc2aNDwQvHz9A0mj3OU9vXwCgjNDsiLzC4oGY4YS60okmZt123q69Q2MzETGFpenV8nWs8NySHbn9%20MTk1LQVAARgE6RC7xykVIACJ0AALTQ4ECaLjoIiI4Dw9hETQ4zQITT0XBwTT4BDcTRgVjoSCsUCaeG2InoCC4rEQTToABGACsIPZgGT0OgkGT2ChNIoRZpuQhWDC%20AB6JWUVg01gpcBAgBMYOCmTCUO2RVhCKRKLRGM0WPx%20MJxJwpPJlOptJUDKZNhZbJt8M5PP5guFovFkul6Fl8sVUBVUBs6FYSCIgiAA

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

Note: In the first condition, T and U each infer one sub-term, while V infers the rest of the string.

Update:TS 4.5 会将类型实例化深度限制从 50 提高到 100,因此新版本不需要此编译器技巧。对于更复杂的情况,您现在还可以使用尾递归求值 https://github.com/microsoft/TypeScript/pull/45711.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

TypeScript 将通用对象从蛇形命名法转换为驼峰命名法 的相关文章

随机推荐