TypeScript konverter generisk objekt fra snake case til camel case
Jeg vil gjerne skrive en funksjon som tar imot et objekt med snake case-nøkler og konverterer det til et objekt med camel case-nøkler. Hva er den beste måten en slik funksjon kan typiseres på i TypeScript, forutsatt at vi kjenner typen til inngangsobjektet, men ønsker at løsningen skal være generisk.
type InputType = {
snake_case_key_1: number,
snake_case_key_2: string,
...
}
function snakeToCamelCase(object: T): U {
...
}
Hva er den beste jobben som kan gjøres for å skrive T og U.
Jeg ønsker at U skal være så snevert typet som mulig, og at typen til U helst skal være basert på T.
Ideelt sett, hvis T er min eksempel InputType, vil jeg at U skal være typet som
{
snakeCaseKey1: number,
snakeCaseKey2: string,
...
}
Svar #1
Løsning
Lekeplass
Dette er mulig med malbokstype-litteraler i TypeScript 4.1 (se også 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)
Du vil da kunne bruke tastemapping i kartlagte typer for å konstruere en ny posttype:
type OutputType = {[K in keyof InputType as SnakeToCamelCase<K>]: InputType[K]}
/*
type OutputType = {
snakeCaseKey1: number;
snakeCaseKey2: string;
}
*/
Utvidelser
Inversjonstype
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-case, kebab-case og inversjoner
Når du har fått de ovennevnte typene, er det ganske enkelt å konvertere mellom dem og andre kasus ved å bruke de innebygde strengtypene Capitalize og 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>>
For kebab case, erstatt _ fra snake case-typen med -.
Konverter innlagte egenskaper
type SnakeToCamelCaseNested<T> = T extends object ? {
[K in keyof T as SnakeToCamelCase<K & string>]: SnakeToCamelCaseNested<T[K]>
} : T
"Type-instansiering er ekstremt dyp og muligens uendelig."
Denne feilen kan oppstå med ganske lange strenger. Du kan behandle flere del-termer på en gang for å begrense typprosesseringens rekursjon til et akseptabelt nivå for kompilatoren. F.eks. SnakeToCamelCaseXXL:
Lekeplass
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
Merk: I den første tilstanden utleder T og U hver sin delterm, mens V utleder resten av strengen.
Oppdatering: TS 4.5 vil øke typen-instanseringsdybdebegrensningen fra 50 til 100, så dette kompilatortrikset er ikke nødvendig med nyere versjoner. For mer komplekse tilfeller kan du nå også bruke hale-rekursiv evaluering.
Svar #2
For det det er verdt, støtte jeg på noen problemer med typene fra @ford04s svar. Jeg fant CamelCasedProperties og SnakeCasedProperties fra https://github.com/sindresorhus/type-fest til å fungere bra så langt.
