snakeCase.ts

 1import { snakeCase } from "case-anything"
 2
 3// https://stackoverflow.com/questions/60269936/typescript-convert-generic-object-from-snake-to-camel-case
 4
 5// Typescript magic to convert any string from camelCase to snake_case at compile time
 6type SnakeCase<S> = S extends string
 7    ? S extends `${infer T}${infer U}`
 8        ? `${T extends Capitalize<T> ? "_" : ""}${Lowercase<T>}${SnakeCase<U>}`
 9        : S
10    : S
11
12type SnakeCased<Type> = {
13    [Property in keyof Type as SnakeCase<Property>]: SnakeCased<Type[Property]>
14}
15
16export default function snakeCaseTree<T>(object: T): SnakeCased<T> {
17    const snakeObject: any = {}
18    for (const key in object) {
19        snakeObject[snakeCase(key, { keepSpecialCharacters: true })] =
20            snakeCaseValue(object[key])
21    }
22    return snakeObject
23}
24
25function snakeCaseValue(value: any): any {
26    if (typeof value === "object") {
27        if (Array.isArray(value)) {
28            return value.map(snakeCaseValue)
29        } else {
30            return snakeCaseTree(value)
31        }
32    } else {
33        return value
34    }
35}