TypeScript: Everyday Types - discussion (2022-12-30)
The TypeScript handbook chapter on everyday types is basic, but in React codebases it is where a lot of architectural decisions show up: what is a union, what is an object, what is optional, and what becomes a stable contract vs an incidental detail.
What do you treat as the typed contract in a React app: route state, server payloads, or component props? Do you prefer explicit discriminated unions for UI states (loading/empty/error), or a more fluid model with optional fields? How do you keep derived state typed without turning everything into generics soup?
Comments (10)
Back to latestFor me, route state is the contract. Props are just plumbing.
Strongly agree on discriminated unions for user-visible states.
They make it harder to ship a screen that renders half-loaded nonsense.
Related: TypeScript Boundaries (Deep Dive #9).
A pattern we use for UI surfaces:
ts
type LoadState<T> =
| { kind: 'idle' }
| { kind: 'loading' }
| { kind: 'error'; message: string }
| { kind: 'ready'; data: T };
Then we store derived evidence like lastKind or lastUpdatedAt so support can reason about what happened.
Counterpoint: optional-field models can be fine when you want flexibility.
Unions can become verbose when there are a lot of intermediate states.
I get that. My rule is: if the user can observe the state change, it deserves a discriminant.
If it is internal plumbing, optional fields are usually fine.
That distinction is helpful. We got burned by a screen that was both loading and error at once.
On derived state typing: we stopped trying to make the derivation generic.
Instead, we typed the document shape and kept derivations as explicit functions that write typed keys.
A misunderstanding from our team: they thought TS would prevent invalid UI states by itself.
It only helps if you encode the actual states, which is why unions are worth it.
Our biggest win was typing server payloads as contracts and treating route state as the adapter layer.
If you are doing the forum docs style of stored derived state, TS helps a lot.
If visibleItems is part of the doc shape, the render stays stable and the type checker keeps you honest about writes.
Everyday types is where the real decisions happen: optionality and unions are basically your UI contract language.
If it is a contract, type it. If it is an implementation detail, do not pretend it is stable.