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 }orif (user !== undefined). Inside the block the type is narrowed toUser. - Optional chaining:
user?.address?.citystops and yieldsundefinedif something is missing. - Nullish coalescing:
user?.name ?? "anonymous"supplies a default fornull/undefinedonly (unlike||, which also replaces""and0). - 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 | undefinedorT | 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.