Transforming Object Keys to camelCase with TypeScript
Add to your RSS feed13 January 20252 min readTable of Contents
In this guide, you'll learn how to transform object keys written in snake_case
into camelCase
using TypeScript. This recipe provides a practical and reusable solution that maintains compatibility with existing backend responses and ensures proper type safety.
Problem: Why We Need Key Transformation
In many projects, backend responses often use snake_case
for key naming. For example, Go-based backends commonly follow this pattern. However, frontend development in JavaScript/TypeScript typically prefers camelCase
.
This discrepancy can lead to mismatches and additional transformation layers in code, especially when auto-generated types (e.g., Swagger) are involved. Instead of manually handling these discrepancies, we can automate the process by transforming keys directly in TypeScript types.
Step 1: Convert Strings to camelCase
The first step is to create a recursive utility that converts a single string from snake_case
to camelCase
. Here's the implementation:
1 type CamelCase<S> =2 S extends `${infer First}_${infer SecondFirst}${infer Rest}`3 ? `${First}${Uppercase<SecondFirst>}${CamelCase<Rest>}`4 : S;
Explanation:
1. Base Case: If the string contains no underscores (_), it remains unchanged. 2. Recursive Case: Split the string into three parts:
First
: The portion before the first underscore.SecondFirst
: The first character after the underscore, converted to uppercase.Rest
: The remaining part of the string. 3. CombineFirst
, the uppercase version ofSecondFirst
, and recursively processRest
.
Example:
For array_of_strings
, the transformation works as follows:
- Split into
array
,o
, andf_strings
. - Convert to
arrayOf_strings
. - Continue recursively until no underscores remain, resulting in
arrayOfStrings
.
Step 2: Transform Object Keys
Next, create a utility type to recursively transform all keys in an object type to camelCase
.
1 type KeysToCamelCase<T> = T extends Record<string, unknown>2 ? {3 [K in keyof T as CamelCase<K>]: KeysToCamelCase<T[K]>;4 }5 : T extends Array<infer U>6 ? Array<KeysToCamelCase<U>>7 : T;
Explanation:
- Objects: Transform keys using CamelCase and recursively process their values.
- Arrays: Recursively process the array's item type.
- Base Types: Return the original type for non-objects and non-arrays.
Complete Example
Here’s the complete code with a practical use case:
1 // Input Types2 type CityOne = {3 city_one: string;4 };56 type GetUserResponse = {7 id: string;8 user_name: string;9 user_email?: string;10 organization: {11 array_of_strings: ['string_one', CityOne];12 org_units_optional?: string[];13 addresses: CityOne[];14 several_different?: [{ type_one: string }, { type_two: string }];15 };16 };1718 // Transformation Utilities19 type CamelCase<S> =20 S extends `${infer First}_${infer SecondFirst}${infer Rest}`21 ? `${First}${Uppercase<SecondFirst>}${CamelCase<Rest>}`22 : S;2324 type KeysToCamelCase<T> = T extends Record<string, unknown>25 ? {26 [K in keyof T as CamelCase<K>]: KeysToCamelCase<T[K]>;27 }28 : T extends Array<infer U>29 ? Array<KeysToCamelCase<U>>30 : T;3132 // Transformed Type33 type CameledRecord = KeysToCamelCase<GetUserResponse>;
Result:
CameledRecord
has all keys transformed into camelCase
, including nested types and arrays, while preserving optional fields and other type details.
Conclusion
By combining the CamelCase
and KeysToCamelCase
utilities, you can effortlessly transform any TypeScript type from snake_case
to camelCase
. This approach is particularly useful for integrating with backends that follow different naming conventions. With this recipe, you can ensure consistency in your frontend code while maintaining strong type safety.