React Forum
Blog & Guide Responses
Jun 03, 2023 · 04:00 AM

TypeScript: Narrowing - discussion (2023-06-03)

Started by Rowan Pierce
externaltypescriptnarrowingtype guardsassertsnormalizationcontractsevidence

The Narrowing chapter is where TypeScript starts acting like a policy engine: your if statements and type predicates become part of your app's runtime behavior. In React apps, the question I care about is where narrowing decisions live so they don't drift across surfaces.

Read the handbook

Do you prefer type guards/type predicates, or normalize into a contract object and avoid guards downstream? Where do you put narrowing logic: route docs, shared helpers, or at the leaf components? Do you log narrowing outcomes for user-facing boundaries (URLs/forms) so debugging is possible from a report?

Comments (12)

Back to latest
Hana Saito
Jun 07, 2023 · 07:07 AM

Normalize once at the boundary.

Then components never parse or narrow.

Rafael Soto
Jun 12, 2023 · 12:12 PM

We log narrowing outcomes for URLs because it's the only way to debug weird shared links:

txt
[tips] narrow=query.page raw="-1" result=clamp value=1 reason=outOfRange
[tips] narrow=query.sort raw="" result=drop reason=empty

Narrowing isn't just for TS; it's product behavior.

Mei Tan
Jun 18, 2023 · 06:18 PM

Type guards are fine if they're tiny and centralized.

What breaks is when each component narrows slightly differently and you get inconsistent behavior across routes.

Jun Park
Jul 07, 2023 · 07:07 AM

Concrete alternative: use asserts to fail fast in dev and fallback in prod.

If you only fallback, you may never notice the boundary drift until users do.

Rowan Pierce
Jul 09, 2023 · 09:30 AM

This is what we do too: throw in dev, lane=error + evidence in prod.

Nina Kapoor
Jul 19, 2023 · 07:19 PM

Long-form: narrowing is a drift policy.

If you normalize query params, you are picking winners: clamp vs drop vs error lane. Those decisions should be explicit and reviewable, not embedded in random Number() calls.

Grace McCoy
Aug 21, 2023 · 09:21 PM

Docs tie-in: narrowing pairs with routing/state docs when you treat boundaries as contracts.

Related: TypeScript and Types and Routing and Navigation.

Lina Ortega
Sep 12, 2023 · 12:12 PM

Short take: the best narrowing is the one you don't have to repeat.

Evan Brooks
Oct 18, 2023 · 06:18 PM

We banned parsing in components and it solved most inconsistencies.

Route docs parse, normalize, and then the rest of the app consumes the contract shape.

Ibrahim Saleh
Nov 10, 2023 · 10:10 AM

Counterpoint: local guards are fine for local state, not for boundary state.

If it's coming from URL/network/user input, narrowing should be centralized.

Anika Sharma
Nov 30, 2023 · 06:06 AM

We write unit tests for normalization rules. It's cheap and it prevents subtle regressions.

If narrowing is product behavior, it deserves tests.

Salma Qureshi
Dec 22, 2023 · 10:22 PM

If you change narrowing behavior, log it. Otherwise link debugging becomes archaeology.

Dmitri Kline
Dec 29, 2023 · 11:23 PM

Takeaway: narrowing is where types become policy. Treat it as a boundary contract and keep it observable.

If you keep it implicit, you'll end up with inconsistent behavior that types can't save you from.