export interface LogPushProps { maxFooBar: string; } export function camelToSnakeObjectDeep( obj: T, ): T extends undefined ? undefined : T extends object ? CamelToSnake : T { if (obj === undefined || obj === null) { return obj as any; } // If it's not an object (primitive types), return as-is if (typeof obj !== "object") { return obj as any; } if (obj instanceof RegExp) { return obj as any; } // Handle arrays if (Array.isArray(obj)) { return obj.map(camelToSnakeObjectDeep) as any; } // Handle objects return Object.fromEntries( Object.entries(obj).map(([key, value]) => [ key .replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2") // Handle consecutive capitals: "FOOBar" -> "FOO_Bar" .replace(/([a-z])([A-Z])/g, "$1_$2") // Handle normal camelCase: "fooBar" -> "foo_Bar" .toLowerCase(), Array.isArray(value) ? value.map(camelToSnakeObjectDeep) : typeof value === "object" && value !== null ? camelToSnakeObjectDeep(value) : value, ]), ) as any; } // Helper to check if a character is uppercase type IsUpper = C extends Uppercase ? C extends Lowercase ? false // Not a letter : true // Uppercase letter : false; // Helper to check if a character is lowercase type IsLower = C extends Lowercase ? C extends Uppercase ? false // Not a letter : true // Lowercase letter : false; // Convert a camelCase string to snake_case at the type level type CamelToSnakeString< S extends string, Acc extends string = "", > = S extends `${infer First}${infer Second}${infer Rest}` ? IsUpper extends true ? IsUpper extends true ? // FOO... pattern - check if next char is lowercase Rest extends `${infer Third}${infer _}` ? IsLower extends true ? // FOOBar -> add First, add _, add Second lowercase, continue with Rest CamelToSnakeString< Rest, `${Acc}${Lowercase}_${Lowercase}` > : // FOO -> continue CamelToSnakeString<`${Second}${Rest}`, `${Acc}${Lowercase}`> : // Only two chars left, both upper CamelToSnakeString<`${Second}${Rest}`, `${Acc}${Lowercase}`> : // FooBar or Foo - add underscore before uppercase if not at start CamelToSnakeString< `${Second}${Rest}`, Acc extends "" ? `${Lowercase}` : `${Acc}_${Lowercase}` > : IsLower extends true ? IsUpper extends true ? // fooBar -> just add the lowercase char, underscore will be added when processing uppercase CamelToSnakeString<`${Second}${Rest}`, `${Acc}${First}`> : // foobar -> continue CamelToSnakeString<`${Second}${Rest}`, `${Acc}${First}`> : // Number or special char CamelToSnakeString<`${Second}${Rest}`, `${Acc}${First}`> : S extends `${infer Last}` ? `${Acc}${Lowercase}` : Acc; // Deep transformation for objects type CamelToSnake = T extends object ? T extends Array ? Array> : T extends Date | RegExp | Function ? T // Don't transform special object types : { [K in keyof T as K extends string ? CamelToSnakeString : K]: CamelToSnake; } : T;