React Forum
Blog & Guide Responses
Jan 02, 2023 · 04:00 AM

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

Started by Lena Fischer
externaltypescriptnarrowingtype guardsreactdocs

Narrowing is one of those TS topics that only feels important after you have shipped a bug caused by assuming a value was present. In UI code it shows up constantly: optional props, nullable server fields, and route params that may or may not exist depending on navigation posture.

Read the docs

Do you write explicit type guards in UI code, or rely on ad-hoc checks inline? Where do you prefer to narrow: at the route boundary, inside components, or in a shared adapter layer? How do you keep narrowing from turning into a maze of conditions as the UI grows?

Comments (10)

Back to latest
Avery Chen
Jan 02, 2023 · 04:06 AM

I narrow at the boundary if I can. It keeps components boring.

Dmitri Kline
Jan 02, 2023 · 04:14 AM

Type guards are worth it when the same narrowing logic repeats across surfaces.

Otherwise, you end up with five slightly different checks and they drift.

Related: TypeScript Boundaries (Deep Dive #9).

Salma Qureshi
Jan 02, 2023 · 04:22 AM

Example guard we use a lot for route params:

ts
type Params = { id?: string };

export function hasId(p: Params): p is { id: string } {
  return typeof p.id === 'string' && p.id.length > 0;
}

Then the route can decide whether to redirect, render a missing state, or open a bridge flow.

Keira Santos
Jan 02, 2023 · 04:33 AM

Counterpoint: guards can become a new abstraction layer that hides simple checks.

If it is only used once, I would rather see the check inline.

Lena Fischer
Jan 02, 2023 · 04:38 AM

Agreed. I reach for guards when they encode policy (what counts as valid) rather than just syntax sugar.

If the guard is the policy, it belongs at the boundary.

Keira Santos
Jan 02, 2023 · 04:42 AM

Policy vs convenience is the right split.

Hana Saito
Jan 02, 2023 · 04:54 AM

We also use discriminants as a narrowing tool for UI state, and it pairs well with rendering evidence.

If the discriminant is visible (even as a data attribute), debugging becomes much less guessy.

Maya Henson
Jan 02, 2023 · 05:03 AM

A misunderstanding in my team: people thought if (value) was a valid guard for everything.

It breaks down fast with empty strings, zero, and deliberate falsy values.

Grace McCoy
Jan 02, 2023 · 05:18 AM

For UI, the best narrowing is often turning unknowns into a stable route shape early.

Then components only render what the route contract guarantees.

Camille Duarte
Jan 02, 2023 · 05:29 AM

I like guards for server payloads too. Narrow once, then store a derived-ready shape in the document so rendering stays consistent.

Rowan Pierce
Jan 02, 2023 · 05:42 AM

We got rid of a bunch of UI edge bugs by narrowing in one adapter function and making the adapter the only place we touched raw payloads.

It also made the codebase easier for new hires.

Benji Rios
Jan 02, 2023 · 05:55 AM

Narrowing is basically contract enforcement. If you care about route contracts, you should care about narrowing.