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 the 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 '' is falsy, and booleans, where false is falsy. A form field left blank and a form field never submitted are two different situations. 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 != treats the two as equal, so one comparison covers both. TypeScript understands the idiom and narrows correctly. Use it on purpose, and tell your team why, 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, the only type they share:

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 from you.

What happens without any check

Skip the narrowing and call a method directly on count, and strict null checking stops you:

'count' is possibly 'undefined'.

That error is the safety net. The two checks in this lesson are both valid ways to satisfy it. The difference is only in which valid values survive.

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

Try this: write a function accepting string | undefined that keeps an empty string as-is and replaces only undefined with 'Unknown'.

Lesson completed