I want to create a function in Typescript that traverses deeply a Plain object and replaces all snake_case keys with camelCase. Also I want to create a function that converts camelCase keys to snake_case deeply.
Implementing this in JavaScript is easy, but it's hard for me because I have to consider types.
How do I write this in TypeScript?
My JS version:
const keyToSnakeCase = obj => {
if (Array.isArray(obj)) {
return obj.map(el => keyToSnakeCase(el));
}
if (!isPlainObject(obj)) {
return obj;
}
const newObj = {};
Object.entries(obj).forEach(([key, value]) => {
newObj[camelCase(key)] = keyToSnakeCase(value);
});
return newObj;
};
const keyToCamelCase = obj => {
if (Array.isArray(obj)) {
return obj.map(el => keyToCamelCase(el));
}
if (!isPlainObject(obj)) {
return obj;
}
const newObj = {};
Object.entries(obj).forEach(([key, value]) => {
newObj[snakeCase(key)] = keyToCamelCase(value);
});
return newObj;
};
Thanks to @jcalz, I solved the problem.
import isPlainObject from "lodash-es/isPlainObject";
import camelCase from "lodash-es/camelCase";
import snakeCase from "lodash-es/snakeCase";
type SnakeToCamel<T extends string> = T extends `${infer F}_${infer R}`
? `${Lowercase<F>}${Capitalize<SnakeToCamel<R>>}`
: T;
type CamelToSnake<
T extends string,
A extends string = ""
> = T extends `${infer F}${infer R}`
? CamelToSnake<R, `${A}${F extends Lowercase<F> ? F : `_${Lowercase<F>}`}`>
: A;
type DeepCamelKeys<T> = T extends readonly any[]
? { [I in keyof T]: DeepCamelKeys<T[I]> }
: T extends object
? {
[K in keyof T as K extends string ? SnakeToCamel<K> : K]: DeepCamelKeys<
T[K]
>;
}
: T;
type DeepSnakeKeys<T> = T extends readonly any[]
? { [I in keyof T]: DeepSnakeKeys<T[I]> }
: T extends object
? {
[K in keyof T as K extends string ? CamelToSnake<K> : K]: DeepSnakeKeys<
T[K]
>;
}
: T;
function keyToSnakeCase<T>(obj: T): DeepSnakeKeys<T>;
function keyToSnakeCase(obj: any) {
if (Array.isArray(obj)) {
return obj.map((el) => keyToSnakeCase(el));
}
if (!isPlainObject(obj)) {
return obj;
}
const newObj: any = {};
Object.entries(obj).forEach(([key, value]) => {
newObj[camelCase(key)] = keyToSnakeCase(value);
});
return newObj;
}
function keyToCamelCase<T>(obj: T): DeepCamelKeys<T>;
function keyToCamelCase(obj: any) {
if (Array.isArray(obj)) {
return obj.map((el) => keyToCamelCase(el));
}
if (!isPlainObject(obj)) {
return obj;
}
const newObj: any = {};
Object.entries(obj).forEach(([key, value]) => {
newObj[snakeCase(key)] = keyToCamelCase(value);
});
return newObj;
}