React Forum
Blog & Guide Responses
Feb 08, 2023 · 04:00 AM

TypeScript: Narrowing - discussion (2023-02-08)

Started by Talia Moss
externaltypescriptnarrowingdiscriminated unionsguardsintent objectslanes

Narrowing is where TypeScript stops being decoration and becomes a UI correctness tool. When you model lanes and intent objects, narrowing keeps reducers and handlers honest: you can't "forget" a state without the compiler making noise. I'm curious what narrowing patterns people actually rely on day-to-day.

Read the handbook

Do you use discriminated unions for async lanes and intent objects, or keep separate flags? What narrowing patterns keep complex handlers readable (kind, type guards, asserts)? Do you treat narrowing as part of your evidence/logging posture (typed vocab -> stable logs)?

Comments (12)

Back to latest
Marisol Vega
Feb 11, 2023 · 11:11 AM

Discriminated unions for lanes is the biggest win. Booleans always drift into impossible combinations.

Dmitri Kline
Feb 14, 2023 · 02:14 PM

We narrow by kind and then render evidence from the narrowed shape. It makes UI truth and type truth line up:

ts
type Lane = { kind: 'pending'; reason: string } | { kind: 'ok'; freshnessAt: string } | { kind: 'error'; message: string };

function laneEvidence(l: Lane) {
  if (l.kind === 'pending') return `pending:${l.reason}`;
  if (l.kind === 'ok') return `ok:${l.freshnessAt}`;
  return `error:${l.message}`;
}

And the log lines become stable because they share the same vocabulary:

txt
[tips] lane=pending reason=route:enter
[tips] lane=ok freshnessAt=2023-02-14T14:14Z
Keira Santos
Feb 18, 2023 · 06:06 AM

Counterpoint: unions can get unreadable if you over-model everything.

We keep unions for state machines and keep the rest as plain objects. Otherwise types become their own app.

Talia Moss
Feb 21, 2023 · 04:04 AM

Same threshold: if it crosses boundaries (route, persistence, panels), model it. If it's local and ephemeral, keep it simple.

Narrowing is a tool, not a lifestyle.

Grace McCoy
Mar 01, 2023 · 03:03 AM

asserts helpers are underrated for runtime boundaries (query params, JSON).

If you don't validate the boundary, narrowing doesn't protect you.

Jasper Nguyen
Mar 14, 2023 · 02:14 PM

Long-form: narrowing is also a readability tool because it forces you to pick a vocabulary.

Once you pick a vocabulary (kind, intent.type, posture), you can align UI evidence and logs with it. That alignment is what makes large systems debuggable.

Nina Kapoor
Mar 29, 2023 · 03:03 AM

We use narrowing for event handlers too, but only lightly (early returns).

Trying to perfectly type DOM events made the code worse than runtime checks.

Camille Duarte
Apr 18, 2023 · 06:18 PM

Counterpoint: if you type and narrow everything, people start using as casts to escape.

We keep types small and let narrowing be shallow. Otherwise the system becomes self-defeating.

Owen Price
May 10, 2023 · 10:10 AM

We model route keys as unions and it prevented "mystery panel" bugs.

When the panel is typed, the UI can render evidence and tests can assert it reliably.

Benji Rios
Jun 01, 2023 · 06:06 AM

Long-form counterpoint: the biggest risk isn't missing a case, it's having too many cases that mean the same thing.

If you have 7 lanes but only 3 UI behaviors, you're modeling internal implementation, not product truth.

Lina Ortega
Jun 25, 2023 · 10:22 PM

Docs tie-in: narrowing + evidence posture is where TS becomes a product quality lever.

Related: TypeScript and Types and Testing and Debugging.

Salma Qureshi
Dec 31, 2023 · 09:09 AM

Narrowing forces teams to agree on states. That's the real value.

Once states are agreed, you can design UI and logs that actually explain the system.

Tomas Rivera
Dec 20, 2023 · 06:18 PM

If you're not sure where to start: model your async lane as a union and write one function that renders evidence from it.

That one move improves code review, debugging, and tests immediately.