A junior writes toCamel(obj: Record<string, unknown>) and ships. At runtime the transform is three lines: loop, regex, reassign. Types return Record<string, unknown>. Autocomplete dies.
Your backend ships { 'user-id': 1, 'first-name': 'Vij' }. Your frontend wants js.userId and js.firstName — autocomplete-friendly, type-safe, no as any casts at every call site. The runtime transform is three lines of string.replace. The type-level transform is where the runtime and the compiler stop being friends. The runtime knows 'user-id' becomes 'userId' because it CAN see the hyphen. The compiler, up to this point in your career, could not.
// Attempt 1 — the naive pass-through. It compiles.// It also does nothing at the type level.type KebabToCamel<S extends string> = Stype Out = KebabToCamel<'hello-world-foo'>// ^? 'hello-world-foo' ← unchanged. No transform.hello-world-foo'hello-world-foo'failuser-id'⟶ 'userId'pendingfirst-name'⟶ 'firstName'pendinglast-login-at'⟶ 'lastLoginAt'pendingalready-camel-case'⟶ 'alreadyCamelCase'pendingThe playground above has four panels. The top is a recipe editor — it'll fill in, stencil by stencil, as you work through three checkpoints. Below that, an evaluator shows what the CURRENT recipe produces when you hand it 'hello-world-foo' — our canonical three-segment test string. Then the six-fixture test suite that'll go from red to green as the recipe assembles. Then a hint ladder for when you need it. Read the recipe pane. It's the naive first attempt — a one-line type alias that compiles cleanly and achieves nothing.
Read the recipe above carefully. type KebabToCamel<S> = S is a one-liner — the parameter on the left, the same parameter returned on the right. If we instantiate it with 'hello-world-foo', what type does the compiler report for Out?