Skip to content

Instantly share code, notes, and snippets.

@IPRIT
Created July 25, 2022 14:00
Show Gist options
  • Save IPRIT/7d520ff10d53cb0db888efbdb3121d90 to your computer and use it in GitHub Desktop.
Save IPRIT/7d520ff10d53cb0db888efbdb3121d90 to your computer and use it in GitHub Desktop.
type CapitalLetters =
| 'A'
| 'B'
| 'C'
| 'D'
| 'E'
| 'F'
| 'G'
| 'H'
| 'I'
| 'J'
| 'K'
| 'L'
| 'M'
| 'N'
| 'O'
| 'P'
| 'Q'
| 'R'
| 'S'
| 'T'
| 'U'
| 'V'
| 'W'
| 'X'
| 'Y'
| 'Z'
export type ToCamel<S extends string | number | symbol> = S extends string
? S extends `${infer Head}_${infer Tail}`
? Head extends ''
? Tail extends ''
? '_'
: Tail extends `_${infer CAPS}`
? `_${Capitalize<ToCamel<CAPS>>}`
: `${Capitalize<ToCamel<Tail>>}`
: Tail extends `_${infer CAPS}`
? CAPS extends ''
? `${Uncapitalize<Head>}__`
: `${Uncapitalize<Head>}_${Capitalize<ToCamel<CAPS>>}`
: `${Uncapitalize<Head>}${Capitalize<ToCamel<Tail>>}`
: S
: never
export type ToCamelSafe<S extends string | number | symbol> = S extends string
? ToSnake<ToCamel<S>> extends S
? ToCamel<S>
: `$${S}`
: never
export type UnwrapSafe<S extends string> = S extends `$${infer Body}` ? Body : S
export type ToSnakeSafe<S extends string | number | symbol> = S extends `$${infer Body}` ? `${Body}` : ToSnake<S>
export type ToSnake<S extends string | number | symbol> = S extends string
? S extends `${infer Head}${CapitalLetters}${infer Tail}` // string has a capital char somewhere
? Head extends '' // there is a capital char in the first position
? Tail extends ''
? `_${Lowercase<S>}` /* 'A' */
: S extends `${infer Caps}${Tail}` // tail exists, has capital characters
? Caps extends CapitalLetters
? Tail extends CapitalLetters
? `_${Lowercase<Caps>}_${Lowercase<Tail>}` /* 'AB' */
: Tail extends `${CapitalLetters}${string}`
? `${ToSnake<Caps>}_${ToSnake<Tail>}` /* first tail char is upper? 'ABcd' */
: `${ToSnake<Caps>}${ToSnake<Tail>}` /* 'AbCD','AbcD', */ /* TODO: if tail is only numbers, append without underscore */
: never /* never reached, used for inference of caps */
: never
: Tail extends '' /* 'aB' 'abCD' 'ABCD' 'AB' */
? S extends `${Head}${infer Caps}`
? Caps extends CapitalLetters
? Head extends Lowercase<Head> /* 'abcD' */
? `${ToSnake<Head>}${ToSnake<Caps>}`
: never /* stop union type forming */
: never
: never /* never reached, used for inference of caps */
: S extends `${Head}${infer Caps}${Tail}` /* 'abCd' 'ABCD' 'AbCd' 'ABcD' */
? Caps extends CapitalLetters
? Head extends Lowercase<Head> /* is 'abCd' 'abCD' ? */
? Tail extends CapitalLetters /* is 'abCD' where Caps = 'C' */
? `${ToSnake<Head>}_${ToSnake<Caps>}_${Lowercase<Tail>}` /* aBCD Tail = 'D', Head = 'aB' */
: Tail extends `${CapitalLetters}${string}` /* is 'aBCd' where Caps = 'B' */
? `${Head}_${ToSnake<Caps>}_${ToSnake<Tail>}` /* 'aBCd' => `${'a'}_${Lowercase<'B'>}_${ToSnake<'Cd'>}` */
: `${ToSnake<Head>}_${Lowercase<Caps>}${ToSnake<Tail>}` /* 'aBcD' where Caps = 'B' tail starts as lowercase */
: never
: never
: never
: S /* 'abc' */
: never
export type ObjectToSnake<T extends unknown, SAFE extends boolean> = T extends undefined
? undefined
: T extends null
? null
: T extends Array<infer ArrayType>
? ArrayType extends Record<string, unknown>
? Array<ObjectToSnake<ArrayType, SAFE>>
: Array<ArrayType>
: T extends Buffer
? Buffer
: {
[K in keyof T as SAFE extends true ? ToSnakeSafe<K> : ToSnake<K>]: T[K] extends
| Array<infer ArrayType>
| undefined
| null
? ArrayType extends Record<string, unknown>
? Array<ObjectToSnake<ArrayType, SAFE>>
: Array<ArrayType>
: T[K] extends Record<string, unknown> | undefined | null
? ObjectToSnake<T[K], SAFE>
: T[K]
}
export type ObjectToCamel<T extends unknown, SAFE extends boolean> = T extends undefined
? undefined
: T extends null
? null
: T extends Array<infer ArrayType>
? ArrayType extends Record<string, unknown>
? Array<ObjectToCamel<ArrayType, SAFE>>
: Array<ArrayType>
: T extends Buffer
? Buffer
: {
[K in keyof T as SAFE extends true ? ToCamelSafe<K> : ToCamel<K>]: T[K] extends Array<infer ArrayType>
? ArrayType extends Record<string, unknown>
? Array<ObjectToCamel<ArrayType, SAFE>>
: Array<ArrayType>
: T[K] extends Record<string, unknown>
? ObjectToCamel<T[K], SAFE>
: T[K]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment