TypeScript Type Gymnastics: Advanced Type Challenges Explained
Level up your TypeScript skills with advanced type-level programming challenges including conditional types, mapped types, and template literals.
Why Type Gymnastics?
TypeScript's type system is Turing-complete — it can express computations at the type level. While you don't need advanced types for everyday code, understanding them helps you write better library APIs, catch more bugs at compile time, and understand the types you encounter in frameworks like Zod, tRPC, and Drizzle.
Challenge 1: Deep Readonly
Make every property in a nested object recursively readonly.
``typescript
type DeepReadonly
// Usage type Config = { server: { port: number; host: string }; features: string[]; };
type FrozenConfig = DeepReadonly
Challenge 2: String to Union Type
Convert a string literal type like \"a.b.c"\ to a union \"a" | "b" | "c"\.
`typescript
type StringToUnion\${infer First}.\${infer Rest}\ =
S extends \
? First | StringToUnion
type Result = StringToUnion<'user.profile.name'>;
// "user" | "profile" | "name"
`
Challenge 3: Type-Safe Event Emitter
Build an event emitter where the event names and payload types are statically checked.
`typescript
type EventMap = {
click: { x: number; y: number };
keypress: { key: string; code: number };
resize: { width: number; height: number };
};
class TypedEmitter
on
emit
const emitter = new TypedEmitter
emitter.on('click', ({ x, y }) => { console.log(x, y); // Types are inferred! });
emitter.emit('click', { x: 10, y: 20 }); // OK
// emitter.emit('click', { key: 'a' }); // Type error!
`
Challenge 4: Path Type for Deep Access
Create a type that generates all valid dot-notation paths for a nested object.
`typescript
type Paths\${Prefix}.\${K}\ | Paths>
: never;
}[keyof T & string]
: never;
type User = { name: string; address: { city: string; zip: string; }; };
type UserPaths = Paths
These challenges demonstrate that TypeScript types are a powerful programming language in their own right. Practice them to write APIs that guide developers toward correct usage and catch errors before runtime.