A backend hands you JSON with snake_case or kebab-case keys ({ "first_name": "Ada" }), but your JavaScript code wants camelCase (obj.firstName). Implement camelCaseKeys(value) — a function that returns a new value with every object key rewritten to camelCase, all the way down through nested objects and arrays of objects. Values are never touched; only keys change.
// value: any JSON-shaped value — object, array, string, number, boolean, null.
// returns: a structurally identical value where every OBJECT KEY has been
// converted from snake_case / kebab-case to camelCase. Values are
// left exactly as-is. Non-object inputs are returned unchanged.
function camelCaseKeys(value: unknown): unknown;
Key conversion: split the key on runs of _ or -, keep the first word lowercase, capitalize the first letter of each following word, and join. user_id becomes userId; background-color becomes backgroundColor; an already-camel key like firstName is left alone.
// Flat object — snake_case keys.
camelCaseKeys({ first_name: 'Ada', last_name: 'Lovelace' });
// → { firstName: 'Ada', lastName: 'Lovelace' }
// Kebab-case keys convert too.
camelCaseKeys({ 'background-color': 'red', 'z-index': 10 });
// → { backgroundColor: 'red', zIndex: 10 }
// Recurses into nested objects AND arrays of objects.
camelCaseKeys({
user_profile: { display_name: 'Ada' },
line_items: [{ product_id: 1 }, { product_id: 2 }],
});
// → {
// userProfile: { displayName: 'Ada' },
// lineItems: [{ productId: 1 }, { productId: 2 }],
// }
// Values are never rewritten — only keys. Note 'user_signed_up' stays as-is.
camelCaseKeys({ event_name: 'user_signed_up' });
// → { eventName: 'user_signed_up' }
// Non-objects pass straight through.
camelCaseKeys(42); // → 42
camelCaseKeys('hello_world'); // → 'hello_world'
'user_signed_up' keeps its underscores; you rewrite the key, not the data.{ items: [{ a_b: 1 }] } must convert a_b inside the array. An array of plain primitives ([1, 2, 3]) comes back unchanged.null, and undefined are returned as-is — they have no keys to convert.null is not an object here. Even though typeof null === 'object', treat null as a passthrough value, not something to recurse into.Date, Map, Set, class instances, or circular references — inputs are plain JSON-shaped data (objects, arrays, and primitives).