Narrowing uncertain values

Narrow with equality, null checks, and truthiness

Choose a check that preserves valid values instead of accidentally discarding empty strings or zero.

An if (value) check narrows a type, but it narrows more than most people intend. Truthiness removes every falsy possibility, not only missing values.

function printCount(count: number | undefined) {
  if (count) {
    console.log(count)
  }
}

Inside the if, TypeScript narrows count to number, and the code compiles. But this branch skips undefined, and it also skips the valid number 0. Call printCount(0) and nothing prints. The compiler cannot flag this, because the narrowing is technically correct — the bug is in what the check throws away.

The same trap hits strings, where the empty string '' is falsy, and booleans, where false is falsy. A form field left blank and a form field never submitted are different situations, and truthiness collapses them into one.

Prefer explicit comparisons

Use an explicit check when zero, false, or an empty string has meaning:

if (count !== undefined) {
  console.log(count)
}

Now printCount(0) prints 0, and TypeScript still narrows count to number inside the branch. The check states exactly what you are excluding, nothing more.

You can remove both null and undefined with value != null. The loose != deliberately treats the two as equal, so one comparison covers both. TypeScript understands the idiom and narrows correctly. Use it intentionally and explain the uncommon loose-equality choice to your team, because most style guides ban != everywhere else.

Equality narrows both sides

Equality can also relate two unions. If a is string | number and b is string | boolean, the branch a === b narrows both to string, their only shared possibility:

function match(a: string | number, b: string | boolean) {
  if (a === b) {
    a.toUpperCase()
    b.toUpperCase()
  }
}

For a === b to be true, both values must have the same type, and string is the only type in both unions. TypeScript follows that logic without any annotation.

My advice: reserve truthiness for values where all falsy inputs genuinely mean “nothing to do”, and reach for !== undefined everywhere else.

Exercise: write a function accepting string | undefined. Preserve an empty string while replacing only undefined with 'Unknown'.

Lesson completed

Take this course offline

Get every free book, course edition, and software download.

Get the download library →