Breaking down this task into smaller subtasks is essential. To begin with, you will need to create a utility type that can convert snake_case
to camelCase
. Let's concentrate on this aspect first.
Take a look at the following:
type Separator = '_'
type Convert<Str extends string, Acc extends string = ''> =
// Verify if Str follows the pattern string_string
(Str extends `${infer Head}${Separator}${infer Tail}`
// If yes, determine whether it is the initial call or not, as we don't want to capitalize part of the string
? (Acc extends ''
// As Acc is empty, this is the starting call and the first part should not be capitalized
? Convert<Tail, `${Acc}${Head}`>
// This is not the starting call, so Head should be capitalized
: Convert<Tail, `${Acc}${Capitalize<Head>}`>)
// As Str does not match the pattern, this is the final call
: `${Acc}${Capitalize<Str>}`)
Now, we can iterate through the interface and replace each key with its converted version:
type Builder<T> = {
[Prop in keyof T as Convert<Prop & string>]: T[Prop]
}
// Transformed object keys:
// {
// oneTwoThreeFourthFiveSixSevenEightNineTen: "hello";
// }
type Result = Builder<{
one_two_three_fourth_five_six_seven_eight_nine_ten: 'hello'
}>
Here's a Playground link with the complete code
To reverse the conversion process:
type Separator = '_'
type IsChar<Char extends string> = Uppercase<Char> extends Lowercase<Char> ? false : true;
type IsCapitalized<Char extends string> =
IsChar<Char> extends true
? Uppercase<Char> extends Char
? true
: false
: false
type Replace<Char extends string> =
IsCapitalized<Char> extends true
? `${Separator}${Lowercase<Char>}`
: Char
type Result2 = Replace<'A'>
type CamelToSnake<
Str extends string,
Acc extends string = ''
> =
Str extends `${infer Char}${infer Rest}` ? CamelToSnake<Rest, `${Acc}${Replace<Char>}`> : Acc
// Converted string: "foo_bar_baz"
type Result = CamelToSnake<'fooBarBaz'>
Access the Playground link here