Annotated solution
Published August 9, 2026The solution
type CamelCase<S extends string> = S extends `${infer H}_${infer T}` ? `${H}${CamelCase<Capitalize<T>>}` : S
The common wrong answer
type CamelCase<S extends string> = S extends `${infer H}_${infer T}` ? `${H}${CamelCase<T>}` : S
The underscore is removed but nothing is capitalised, so `"foo_bar"` becomes `"foobar"`. Deleting the separator is only half the transformation — the case change is the other half.
Line by line
Capitalize<T>One of four intrinsic string types the compiler implements natively — `Uppercase`, `Lowercase`, `Capitalize`, `Uncapitalize`. They have no TypeScript source; they are built into the checker.
CamelCase<Capitalize<T>>Capitalising *before* recursing means each level only has to handle the underscore nearest the front. `"a_b_c"` therefore yields `"aBC"` — the recursion capitalises each remaining segment in turn.
Takeaway
The intrinsic case types make string manipulation at the type level far less painful than it has any right to be. Reach for them before writing a character-by-character recursion.