narrowing Narrowing Basics (truthiness & assignability)
Last updated: 2026-10-22
narrowing Narrowing Basics (truthiness & assignability)
A union type packs several possibilities into one variable, so before using it you must figure out what it actually is at that moment — shrinking the wide type down to a concrete one is called narrowing. TypeScript analyzes control flow (if, switch, truthiness checks) and automatically narrows the variable to the matching branch, after which branch-specific members are safe to use.
| Group | Narrowing & Assertions |
|---|---|
| Kind | Syntax |
| Strict mode | Not required |
ℹ️ Info The editor transpiles TypeScript to JavaScript and runs it (no type checking); see each example's Output for type-check results.
📝 Syntax
function handle(value: string | number | null) {
if (value == null) return; // narrows away null/undefined
if (typeof value === "string") {
value.toUpperCase(); // value is narrowed to string here
} else {
value.toFixed(2); // value is narrowed to number here
}
}
⚙️ Parameters
| == null | Loose equality filters out both null and undefined in one check — the most common null-narrowing idiom |
|---|---|
| truthiness | A truthiness check like if (value) removes null, undefined, 0, "", false and NaN all at once |
Returns:None — narrowing is compile-time control-flow analysis and emits no runtime code.
▶ Example
Edit the code below and click Try it to see the transpiled result:
Output:
Hello, guest
Hello, guest
Hello, anonymous
Hello, Ada (length 3)
(Runtime output after transpilation; name.length is safe inside the last branch because tsc has narrowed name to string — at runtime you only see the value)
💡 Notes
ℹ️ Info Narrowing is purely compile-time: TypeScript tracks types along execution paths (control-flow analysis), and the transpiled JS contains no "narrowing code".
⚠️ Note Truthiness checks also filter out legitimate falsy values: 0, "" and false. When 0 is a valid value (prices, counts), prefer value != null or an explicit typeof check instead of if (value).
💡 Tip Narrowing is most reliable on local variables and consts; narrowing on object properties can be invalidated by function calls (TS can't know whether the call mutated it) — copy the property into a local, or use a type guard function.
🏷️ Related Narrowing & Assertions
💡 Related cases
More cases coming soon.
📚 Related tutorials
❓ FAQ
Q How is narrowing different from a type assertion (as)?
A Narrowing is inferred automatically from the checks you write, so checks and types stay in sync; an assertion is you manually declaring a type with no verification. Prefer narrowing whenever possible — a wrong assertion goes unchecked.
Q Why can obj.flag still be undefined after I already checked if (!obj.flag)?
A For property accesses TS does not keep narrowing across possible side effects — any intervening function call might mutate it. Copy the value into a const local first, then check it.
Q Can one check rule out both null and undefined?
A Yes: value == null (loose equality) filters out both at once; value != null is the inverse. Strict equality === would need two comparisons (=== null and === undefined).