Menu

TypeScript null and undefined: Checks, ?. and ?? Operators

With strictNullChecks, null and undefined are separate types that TypeScript makes you handle. Learn how to check for them, optional chaining (?.), the double question mark (??) and ??=, and the difference between an optional property and | undefined.

This page includes runnable editors - edit, run, and see output instantly.

With strictNullChecks on (it is part of strict), null and undefined are their own types. A string can never be null; a value that might be missing has to say so in its type, like string | null, and TypeScript makes you handle that case before using the value.

After the check, the compiler knows name is a string, so .split is allowed. The rest of this page covers the ways to do that check and the operators that make it short.

strictNullChecks and "Possibly Null" Errors

When a type includes null or undefined, TypeScript refuses to use the value as if it were always there:

index.ts(3,22): error TS18047: 'name' is possibly 'null'.

The undefined version is TS18048, 'x' is possibly 'undefined'. Without strictNullChecks, null and undefined are allowed in every type and this code compiles, then crashes at runtime with a TypeError the first time name is null. That class of bug is the main reason to keep strict on.

Where these types come from in everyday code:

SourceType
arr.find(...)T | undefined
map.get(key)V | undefined
optional property p?: TT | undefined when read
optional parameter x?: TT | undefined inside the function
str.match(re)RegExpMatchArray | null
JSON.parse(text)any, so nothing is checked

Checking for null and undefined

Every check below narrows the type inside the block. Pick the one that matches what you want to exclude.

CheckRemoves from the type
x !== undefinedundefined
x !== nullnull
x != nullnull and undefined
typeof x !== "undefined"undefined
if (x)null and undefined, and also skips the values 0, "", false, NaN

== null is the one place where loose equality is idiomatic: it is true for exactly null and undefined, nothing else. A truthiness check would have treated the empty string as missing, which is often a bug.

Optional Chaining: ?.

a?.b reads b if a is not null or undefined, and otherwise stops and returns undefined. The same operator works for indexes, a?.[i], and for calls, fn?.().

The type of bob.address?.city is string | undefined: optional chaining adds undefined to the result, so you usually pair it with ??. The runtime behavior is plain JavaScript; see optional chaining for the details of short-circuiting.

The Double Question Mark: ??

a ?? b returns a unless it is null or undefined, in which case it returns b. It replaces the older a || b idiom, which also discards 0, "", false and NaN:

Left valueleft || "d"left ?? "d"
null"d""d"
undefined"d""d"
0"d"0
"""d"""
false"d"false
NaN"d"NaN

For types, ?? removes null and undefined from the left side and unions the rest with the right side, which is why scores.get("Linus") ?? 0 can be assigned to a number.

Nullish Assignment: ??=

a ??= b assigns b to a only when a is null or undefined. Its siblings ||= and &&= assign when the left side is falsy or truthy.

retries: 0 survives ??=, while the empty label is replaced by ||=. After opts.retries ??= 3, TypeScript narrows opts.retries to number for the rest of the function.

Optional Properties vs | undefined

nickname?: string and nickname: string | undefined read the same, but they differ in whether the key must exist:

Use ? when callers may leave the property out, and | undefined when you want every caller to pass it explicitly, even if the value is undefined. The option exactOptionalPropertyTypes (not part of strict) tightens ? further: nickname?: string then rejects { nickname: undefined } and only accepts a missing key or a string. Optional function parameters (x?: number) behave like optional properties: inside the function x is number | undefined.

null or undefined: Which to Use

TypeScript does not force a choice, but mixing both in one codebase means every check has to handle two cases. A common convention:

  • Use undefined (and optional properties) for "not set" in your own types. It is what JavaScript produces by default: missing properties, omitted arguments, find and Map.get misses.
  • Accept null where an API gives it to you: JSON has no undefined, String.prototype.match and many DOM methods return null.
  • Check with == null when a value might be either.

Array indexing is the one gap: users[5] is typed as the element type even when the index is out of range. The noUncheckedIndexedAccess option (not part of strict) adds | undefined to every index access so the compiler catches that too.

Frequently Asked Questions

What does the double question mark mean in TypeScript?

a ?? b is the nullish coalescing operator from JavaScript. It returns a unless a is null or undefined, in which case it returns b. Unlike ||, it keeps other falsy values such as 0, "" and false. TypeScript removes null and undefined from the type of the left side, so when a is string | undefined, a ?? "x" is a string.

How do I check if a value is undefined in TypeScript?

Compare it: if (value !== undefined) { ... }. TypeScript narrows the type inside the block. To rule out both null and undefined in one check, use value != null (loose equality), which is the one place ==/!= is idiomatic. A truthiness check (if (value)) also narrows but skips 0, "" and false.

What does "Object is possibly undefined" mean?

Errors TS18048 ('x' is possibly 'undefined') and TS18047 ('x' is possibly 'null'), or TS2532 (Object is possibly 'undefined') when the value has no simple name, as in getUser().address.city, come from strictNullChecks: the type includes undefined or null, and the code uses the value as if it could not be. Check it first, use optional chaining (x?.name), supply a default with ??, or change the type if the value really cannot be missing.

What is the difference between null and undefined in TypeScript?

They are two separate types with one value each. JavaScript uses undefined for things that were never set (a missing property, an omitted argument, Map.get on a missing key) and APIs use null for an intentional "no value" (JSON, many DOM methods). TypeScript tracks them separately, so string | null does not accept undefined. Many codebases pick undefined for their own code and only accept null at boundaries.

Is an optional property the same as | undefined?

Not quite. name?: string means the property may be missing entirely, and reading it gives string | undefined. name: string | undefined means the property must be present, even if its value is undefined. With exactOptionalPropertyTypes on, name?: string also stops accepting an explicit undefined value.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED