"Cannot read properties of undefined" is the most common runtime error in JavaScript. TypeScript eliminates most of these crashes by treating null and undefined as distinct types that must be handled explicitly. In this lesson you will learn how strictNullChecks works, how narrowing removes null from a type, and how the ?., ?? and ??= operators express "maybe missing" logic in one line.
JavaScript has two "nothing" values. TypeScript gives each its own type:
undefined: a variable that was declared but not assigned, a missing property, a function with no return.null: an intentional "no value", usually set by your code or returned by DOM and database APIs.Without strictNullChecks, both are silently members of every type, and let name: string = null compiles. With the flag on (it is part of strict, which every modern project enables), a value can only be null or undefined if its type says so:
let name: string = null; // Error under strictNullChecks
let nickname: string | null = null; // OK: explicitly nullable
function find(id: number): User | undefined { /* ... */ }The compiler then refuses to let you use a possibly-missing value without a check:
const user = find(7);
user.name; // Error: 'user' is possibly 'undefined'Any ordinary check narrows the type inside the guarded block:
const user = find(7);
if (user) { // truthiness check
console.log(user.name); // user: User
}
if (user !== undefined) { /* user: User */ }
if (user != null) { /* removes both null and undefined */ }
const label = user?.name ?? "Guest";The loose inequality != null is the one place where != is idiomatic in TypeScript: it excludes null and undefined in a single comparison. Early returns also narrow the rest of the function:
function greet(user: User | null): string {
if (!user) return "Hello, stranger";
return `Hello, ${user.name}`; // user is User from here on
}Be careful with truthiness on numbers and strings: if (count) treats 0 and "" as missing. Compare against undefined explicitly when zero or an empty string is a valid value.
?.Optional chaining stops evaluating an expression and returns undefined as soon as it meets null or undefined. It works on properties, element access and calls:
const city = order.customer?.address?.city; // property chain
const first = list?.[0]; // element access
const result = callback?.(42); // call only if definedThe type of the whole expression gains | undefined. This replaces long a && a.b && a.b.c chains and compiles down to equivalent checks when your target is older than ES2020.
?? and ??=?? returns the right-hand side only when the left is null or undefined. It differs from ||, which also replaces 0, "", false and NaN:
const port = config.port ?? 3000; // 0 stays 0
const port2 = config.port || 3000; // 0 becomes 3000, probably a bug
const retries = options.retries ?? 3;
options.timeout ??= 5000; // assign only if currently nullish| Expression | 0 ?? 10 | 0 \|\| 10 | "" ?? "x" | "" \|\| "x" | null ?? 10 |
|---|---|---|---|---|---|
| Result | 0 | 10 | "" | "x" | 10 |
Use ?? for defaults on values that may legitimately be falsy; use || only when any falsy value should be replaced.
Some APIs return possibly-missing values that beginners overlook:
array.find() and array.pop() return T | undefined.map.get(key) returns V | undefined.prop?: T) read as T | undefined.document.querySelector() returns Element | null.T | undefined when noUncheckedIndexedAccess is enabled.Handle each with narrowing, ?./??, or a deliberate ! (covered in the previous lesson) when you can prove the value exists.
|| for numeric defaults and silently turning 0 into the fallback.?. everywhere, which hides bugs: only the parts that can genuinely be missing should be optional.strictNullChecks to make errors go away. The errors are the feature.Under `strictNullChecks`, which declaration compiles?
strictNullChecks makes null and undefined distinct types that must be declared and handled.!== undefined, != null and early returns all narrow a nullable type.?. short-circuits property, index and call chains to undefined instead of throwing.?? supplies a default only for null/undefined; || also replaces 0, "" and false.find, get, querySelector and optional properties all produce possibly-missing values.Next lesson: Union Types, Intersection Types and Type Narrowing — combine types and let control flow narrow them.