null, undefined and strictNullChecks

TypeScript · Essentials · 10 min

Reading a property of undefined is JavaScript's most common crash; strict null checking eliminates it at compile time.

With strictNullChecks (part of strict), null and undefined are separate types. A string is always a string; something that may be missing must say so: string | undefined or User | null.

Many APIs return possibly-missing values, and TypeScript types them that way: array.find(...) returns T | undefined, map.get(key) returns V | undefined, and document.querySelector(...) returns Element | null. The compiler won't let you use the result as if it were definitely there.

Ways to deal with it:

  • Check first: if (user) { user.name } or if (user !== undefined). Inside the block the type is narrowed to User.
  • Optional chaining: user?.address?.city stops and yields undefined if something is missing.
  • Nullish coalescing: user?.name ?? "anonymous" supplies a default for null/undefined only (unlike ||, which also replaces "" and 0).
  • Early return: if (!user) return; narrows the rest of the function.

There is also the non-null assertion operator value!, which tells the compiler "trust me, this isn't null". It is erased at runtime and checks nothing, so a wrong ! becomes exactly the crash strict mode was meant to prevent. Use it only when you truly know better — and prefer a check that throws a clear error.

Convention: many codebases use undefined for "absent" (it's what optional properties and missing returns produce) and keep null for values that APIs explicitly return as null. Be consistent within a codebase.

Example

interface User { name: string; manager?: User }

const users: User[] = [{ name: "Ada" }, { name: "Grace", manager: { name: "Ada" } }];

const found = users.find(u => u.name === "Linus");   // User | undefined
// @ts-expect-error -- 'found' is possibly 'undefined'
console.log(found.name);

if (found) console.log(found.name);                   // narrowed to User

const bossName = users[1]?.manager?.name ?? "no manager";
console.log(bossName);                                // "Ada"

function requireUser(name: string): User {
  const u = users.find(x => x.name === name);
  if (!u) throw new Error(`No user ${name}`);         // clear failure
  return u;                                           // User
}
console.log(requireUser("Ada").name);

Remember

  • Under strict mode, possibly-missing values are typed T | undefined or T | null
  • Narrow with checks or early returns; use ?. and ?? for concise access
  • ?? only replaces null/undefined; || also replaces 0 and ""

Watch out for

  • Sprinkling ! non-null assertions to silence errors — they check nothing at runtime.

Check yourself

  1. What is the type of [1, 2, 3].find(n => n > 2) under strict mode?
  2. count is typed number | undefined and is 0. What does count ?? 10 evaluate to?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.typescriptlang.org/docs/handbook/2/everyday-types.html#null-and-undefined