strictNullChecks

Gives null and undefined their own types instead of letting them inhabit every other type.

Signature
"strictNullChecks": boolean  // Default: true if strict; false otherwise
  • Added in TypeScript 2.0

Parameters

ParameterTypeDescription
$strictNullChecksoptionalboolean= true if strict; false otherwiseWhen true, null and undefined are distinct types and are not assignable to other types unless the type explicitly includes them.

Errors

Nothing at runtime; it reports errors such as 2531 'Object is possibly null' and 18048 'X is possibly undefined' at compile time.

Changelog

  • PHP 4.3Using a Promise that always appears defined inside a condition check became an error under this flag (always-truthy promise checks).
  • PHP 2.7strictPropertyInitialization depends on this flag; enabling property initialisation checks without strictNullChecks has no effect.

With strictNullChecks off, null and undefined are members of every type. const n: number = null compiles, and Array.prototype.find is typed as returning the element rather than the element or undefined. The compiler is silent about the single most common runtime crash in JavaScript.

With it on, they are distinct types. Anything that can be absent must say so — string | undefined, User | null — and you must narrow before use.

How you narrow

  • if (x) or if (x !== undefined) — control-flow narrowing
  • x?.prop and x ?? fallback — optional chaining and nullish coalescing
  • x! — the non-null assertion, which silences the compiler without proving anything

What trips people up

This is the flag that changes the most types, so it is usually the hardest one to turn on mid-project. Two migration traps: a codebase that compiled without it tends to be riddled with ! once it is on, which just moves the crash back to runtime; and library .d.ts files are checked under your setting, so a dependency's signatures suddenly grow | undefined the day you enable it.

Example

Example · typescript
// "strictNullChecks": true
const users = [
  { name: "Oby", age: 12 },
  { name: "Heera", age: 32 },
];

// find is typed { name: string; age: number } | undefined
const found = users.find((u) => u.name === "Heera");
const missing = users.find((u) => u.name === "Zed");

// console.log(found.age) is error 18048: 'found' is possibly 'undefined'
console.log(found?.age ?? "not found");   // 32
console.log(missing?.age ?? "not found"); // not found

When to use it

  • Catching a possibly-undefined result from find, get, pop or a regex match before it reaches production
  • Modelling an API field that is genuinely absent as string | null instead of pretending it is always there
  • Making optional chaining meaningful instead of decorative

Frequently asked questions

What does 'Object is possibly undefined' mean and how do I fix it?

It means strictNullChecks is on and the value's type includes undefined, so you must narrow before using it. Use a guard such as if (x), optional chaining x?.prop, or a default with x ?? fallback; avoid x! unless you can prove the value exists.

Is strictNullChecks on by default?

It is true when strict is enabled and false otherwise. A tsconfig created by tsc --init sets strict to true, so new projects get it, but a hand-written config that omits both flags does not.

How do I turn on strictNullChecks in an existing project?

Enable it, count the errors, and fix them file by file — or use a tool that adds | undefined to the offending declarations. Resist mass-applying the non-null assertion operator, because it silences the compiler without removing the crash.

More examples

Why the API surface changes

Prints 443 then -1. Without the flag the annotation number | undefined is legal but pointless, and deleting the guard still compiles — the -1 branch would be replaced by a crash on the caller's side.

Example · typescript
// A simplification of what lib.es5.d.ts declares for find:
//
// strictNullChecks: true   find(...): T | undefined
// strictNullChecks: false  find(...): T

const ports: Record<string, number> = { http: 80, https: 443 };

function portFor(name: string): number {
  const p: number | undefined = ports[name];
  if (p === undefined) return -1; // required once the flag is on
  return p;
}

console.log(portFor("https")); // 443
console.log(portFor("ftp"));   // -1

The non-null assertion escape hatch

bad logs undefined even though its declared type is number: the assertion removed the compile-time error without changing the value. good shows the fix — supply a default rather than assert.

Example · typescript
interface Config { retries?: number }

function load(): Config {
  return {}; // the retries key is genuinely absent
}

const cfg = load();

// `!` tells tsc "trust me" — it erases at runtime and proves nothing.
const bad = cfg.retries!;
const good = cfg.retries ?? 3;

console.log(bad);  // undefined
console.log(good); // 3

Discussion

  • Be the first to comment on this lesson.