TypeScript Null

TypeScript's null and undefined handling, powered by strictNullChecks, optional chaining, and nullish coalescing, helps you catch missing-value bugs at compile time instead of at runtime.

null vs undefined

TypeScript treats null and undefined as their own distinct types. undefined typically means a value was never assigned, while null usually signals an intentional absence of a value. Both can appear in a union with any other type.

Two distinct empty values

let a: string | null = null;
let b: number | undefined = undefined;

a = "hello";
b = 42;

console.log(a, b);

strictNullChecks

With strictNullChecks enabled in tsconfig.json, null and undefined are only assignable to their own type (and to any, unknown, or void) unless a type's definition explicitly includes them in a union. This forces you to narrow before using a possibly-missing value.

Narrowing before use

function greet(name: string | null) {
  // console.log(name.toUpperCase()); // Error: 'name' is possibly 'null'
  if (name !== null) {
    console.log(name.toUpperCase()); // OK, narrowed to string
  } else {
    console.log("Hello, stranger!");
  }
}

greet("Ada");
greet(null);

Optional Chaining (?.) and Nullish Coalescing (??)

  • ?. short-circuits to undefined the moment any link in the chain is null or undefined
  • ?. works on property access, method calls, and computed/array indexing
  • ?? only falls back when the left side is null or undefined, not on other falsy values
  • ?. and ?? are commonly chained together to read a deep, optional path with a default
  • ??= assigns a value only when the variable is currently null or undefined

Reading a deep optional path

interface Profile {
  address?: {
    city?: string;
  };
}

function getCity(profile: Profile): string {
  return profile.address?.city ?? "Unknown city";
}

console.log(getCity({}));                            // "Unknown city"
console.log(getCity({ address: {} }));                // "Unknown city"
console.log(getCity({ address: { city: "Delhi" } })); // "Delhi"

let retries: number | undefined;
retries ??= 3;
console.log(retries); // 3
OperatorFalls back onTypical use
||any falsy value (0, "", false, null, undefined, NaN)legacy default values
??only null or undefinedsafe defaults that keep 0/"" valid
?.stops and returns undefinedreading possibly-missing nested paths
Note: Prefer ?? over || whenever 0, an empty string, or false are legitimate values you don't want accidentally replaced by a default.
Note: Optional chaining prevents a crash by short-circuiting, but it doesn't fix the underlying missing data. Reaching for the non-null assertion operator (!) to silence a strictNullChecks error just moves the crash to runtime.

Exercise: TypeScript Null

With `strictNullChecks` enabled, where can `null` and `undefined` be assigned?