Advanced TypeScript Patterns for React
React gives you a runtime model; TypeScript gives you a design language for that model. The gap between “it works” and “it cannot be misused” is where senior teams spend their time—and where advanced TypeScript patterns pay for themselves in fewer production bugs, clearer APIs, and faster refactors.
This chapter is not a refresher on interface versus type or how to type useState. It assumes
you already reach for strict mode, understands structural typing, and have shipped real components.
Here we focus on patterns that encode intent in the type system so that illegal combinations of
props, impossible UI states, and context misuse are rejected before code review.
You will see five interconnected ideas:
Discriminated unions turn vague “optional props for every scenario” APIs into small state machines. The compiler becomes your product owner: if a variant needs
onRetry, callers cannot forget it whenvariantis"error".Generic components let you build tables, lists, and pickers once while preserving row types end-to-end. Inference from
dataand constraints likeT extends { id: string }keep generics ergonomic instead of ceremonial.Polymorphic components with an
asprop let one styled primitive render as abutton,a, or custom component without surrendering toany.ComponentPropsWithoutRefis the bridge between your design system and the DOM’s actual attribute rules.Template literal and mapped types generate prop names, class tokens, and route param unions from a single source of truth. Used judiciously, they eliminate stringly-typed drift; used carelessly, they can explode compile times.
Type-safe context and hooks close the loop: global or cross-cutting state should fail loudly when misused (
useThemeoutside a provider), and custom hooks likeuseLocalStorage<T>should propagateTas faithfully asuseState.
Together, these patterns share one goal: make incorrect usage unrepresentable. The sections that follow go deep on each, with production-shaped examples you can adapt directly.