cotalks.dev

A Deep Dive Into Advanced TypeScript - A Live Coding Expedition by Christian Wörz

(link)
Channel: Devoxx

Summary

Christian Wörz demonstrates advanced TypeScript techniques through live coding, focusing on how to model richer compile-time guarantees without sacrificing developer ergonomics. The talk walks through mapped types, template literal types, `never`, `satisfies`, `infer`, recursive conditional types, tuple construction, and branded types for safer primitives. He also shows how these patterns relate to runtime validation and schema libraries such as Zod. The core theme is using TypeScript to derive types from source models, encode invariants, and reduce duplication. The examples emphasize exhaustive checks, generated event handler names, constrained string formats, recursive unwrapping, and branded scalar types like email, UUID, and integers.

Key Takeaways

  • Use mapped types and template literal types to derive new APIs from a base type, such as generating `onAdd`, `onRemove`, and `onMove` handler names from event keys.
  • Template literal types can model constrained string formats and combinations, such as margin/padding property names or numeric values with units.
  • `never` and `satisfies` are useful for exhaustive control-flow checks, helping enforce that all union cases are handled while still keeping runtime error paths.
  • `infer` enables recursive conditional types, which TypeScript uses for utilities like `Awaited` and can be applied to custom types such as nested arrays.
  • Branded types let you distinguish values like `email` from plain `string` at compile time while still passing them to normal string APIs.
  • Runtime validation and compile-time typing are complementary; for important invariants, pair branded types with guards, assertions, or schema libraries like Zod.

Sections

Mapped Types for Derived APIs

The talk starts with mapped types as a way to keep related types in sync. Christian shows how to generate an event subscription shape from a base `events` type by iterating over `keyof` and reshaping keys with `as` and template literals. He uses `Capitalize` to produce handler names like `onAdd`, demonstrating how mapped types prevent drift when the source model changes.

Template Literal Types for String Patterns

Template literals are used beyond simple key remapping. The speaker builds types for combinations such as `marginLeft`, `paddingTop`, and similar property names by combining unions inside template literal types. He also shows constrained value types like numbers with allowed suffixes such as `%` or `px`, and discusses using mapped types with optional properties when only some keys should be required.

Exhaustive Checks with `never` and `satisfies`

Christian uses `never` to explain unreachable code paths and exhaustive control flow. A switch over a union like `Zurich | Oslo` can be validated so every case returns the expected type. He then introduces `satisfies` to preserve compile-time exhaustiveness even when a default branch throws at runtime, keeping safety in both JavaScript execution and TypeScript analysis.

Recursive Conditional Types and `infer`

The session moves into recursive type programming with `infer`. Using `Awaited` as a reference point, he builds a custom recursive type to unwrap nested arrays. The example shows how `infer` extracts an inner type from a conditional `extends` clause, letting TypeScript repeatedly peel off layers until it reaches the target type.

Building Tuple Types Recursively

A custom `Tuple` type is implemented as a type-level function that grows a collector array until its length matches a requested size. The example demonstrates recursion, tuple length checks, and union support for sizes such as `3 | 4`. The point is to replace large, unreadable hand-written unions with a reusable type-level abstraction.

Branded Types for Safer Primitives

The final major topic is branded types. Christian shows how a plain `string` can be too permissive for values like emails, and how a brand can distinguish `Email` from ordinary strings while still allowing the value to be passed to any string-based API. He demonstrates type guards and assertion functions to create branded values, and notes that a `unique symbol` can hide the brand from normal autocomplete.

Validation Libraries and Practical Use

The talk closes by connecting branded types to runtime validation libraries such as Zod. Christian shows how schemas can be branded so inferred types carry the brand automatically. He emphasizes that TypeScript types alone are not enough for security or correctness at runtime, so compile-time brands should be paired with validation when data comes from external sources.

Keywords: advanced typescript, mapped types, template literal types, typescript infer, recursive conditional types, never type, satisfies operator, branded types, type guards, assertion functions, unique symbol, exhaustive switch, tuple types, awaited utility type, zod branding, runtime validation, compile-time safety, union types, capitalize intrinsic type, type-level programming

note