StrictNullChecks in TypeScript
strictNullChecks makes null and undefined their own distinct types. Learn how it catches the most common runtime error in JavaScript and how to handle nullable values safely.
In plain JavaScript, null and undefined can appear anywhere. You can pass them to any function, assign them to any variable, and access properties on values that might be missing.
When JavaScript encounters a null value where it expected an object, it throws a TypeError at runtime. This is the single most common source of production crashes in JavaScript applications.
The strictNullChecks flag in TypeScript makes null and undefined their own distinct types. When enabled, you cannot use them where a concrete value is expected. Every place a value might be missing becomes visible at compile time, not at 3 AM when your error tracker lights up.
What changes when you enable it
Without strictNullChecks, null and undefined are valid values for every type. This code compiles without any complaint:
let name: string;
name = "Alice";
name = null;
name = undefined;All three assignments are accepted. The type annotation says string, but the compiler lets you put null and undefined in there anyway. This means every string variable in your program might actually be null, and the type system gives you no help discovering where.
With strictNullChecks enabled, the story changes. Null and undefined are no longer part of every type:
let name: string;
name = "Alice"; // OK
name = null; // Error: Type 'null' is not assignable to type 'string'The second assignment now produces a compile error. If you want a variable to accept null, you must declare it explicitly as a union type:
let name: string | null;
name = null; // OK: null is part of the declared typeThis makes the possibility of null visible in the type itself. Anyone reading the code knows this value might be absent.
The most common places it catches bugs
The flag surfaces nullability in many everyday APIs that can return missing values. Before strictNullChecks, these APIs silently pretend the value is always present. After, they tell the truth.
Array.find and Array.findIndex
The .find() method returns the first matching element or undefined if nothing matches. With strictNullChecks on, the return type includes undefined:
const users = [
{ name: "Alice", role: "admin" },
{ name: "Bob", role: "editor" },
];
const admin = users.find((u) => u.role === "superadmin");
console.log(admin.name);The compiler flags the last line, because nothing in the array has the role "superadmin" so the search can come back empty. Here is the exact error:
'admin' is possibly 'undefined'.Admin might be undefined because no user has the role "superadmin". You must handle the missing case before accessing properties on the result.
Map.get
The .get() method on a Map returns the value or undefined if the key does not exist. With strictNullChecks on, every .get() call produces a type that includes undefined, forcing you to check the result.
Optional properties
When you access an optional property on an object, TypeScript includes undefined in the type. The compiler requires a check before you use the value in a context that does not accept undefined.
DOM queries
document.querySelector returns an element or null. With strictNullChecks enabled, you cannot call methods on the result without checking for null first. This catches cases where a CSS selector does not match any element.
How to handle nullable values
Once strictNullChecks is on, you need patterns for working with values that might be null or undefined. TypeScript provides several tools that make this safe and readable.
Narrowing with if checks
The simplest pattern: check for null or undefined before using the value. TypeScript narrows the type inside the if block:
const admin = users.find((u) => u.role === "admin");
if (admin !== undefined) {
console.log(admin.name); // admin is narrowed to the user type here
}After the check, TypeScript knows admin is not undefined, so accessing .name is safe.
Optional chaining
When you only need a property and do not care whether the parent is null, use optional chaining. If the left side is null or undefined, the whole expression evaluates to undefined instead of throwing:
const adminName = users.find((u) => u.role === "admin")?.name;If .find() returns undefined, adminName becomes undefined instead of causing a runtime error. The type of adminName is string | undefined, so downstream code still needs to handle the missing case.
Nullish coalescing
When you want a fallback value, use the nullish coalescing operator. It returns the right side only when the left side is null or undefined:
const adminName = users.find((u) => u.role === "admin")?.name ?? "No admin found";Now adminName is always a string. The fallback kicks in whenever the find or the name access produces null or undefined.
How it fits into strict mode
strictNullChecks is one of the flags enabled by setting strict to true. It works hand in hand with noImplicitAny: one makes sure every value has a known type, the other makes sure null and undefined are handled within that type system.
In the nine-flag strict family, strictNullChecks and noImplicitAny are the two that most directly change how you write code day to day. For the full picture, see strict mode in TypeScript. To understand how the other half of the pair works, read about noImplicitAny in TypeScript.
Rune AI
Key Insights
- strictNullChecks makes null and undefined their own types, not assignable to everything.
- Without it, null and undefined silently flow through your code and cause runtime crashes.
- Array.find(), Map.get(), and optional properties all return T | undefined with this flag on.
- Use if checks, optional chaining, and nullish coalescing to handle nullable values.
- This flag is part of strict mode but can be toggled individually.
Frequently Asked Questions
What is the difference between null and undefined with strictNullChecks?
Why does array.find() return T | undefined?
How do I handle a value that might be null?
Conclusion
strictNullChecks is arguably the most impactful single flag in TypeScript. It forces you to acknowledge that values can be missing and to handle those cases explicitly. The result is code that crashes far less often at runtime because null and undefined are caught at compile time instead.
More in this topic
TypeScript Compiler Explained
The TypeScript compiler (tsc) turns .ts files into .js files while checking types. Learn what it does, how to use it, and how it fits your workflow.
Set Up ESLint for TypeScript
Install and configure ESLint with typescript-eslint to catch bugs and enforce consistent code style in your TypeScript project.
Pick Utility Type in TypeScript
Pick creates a new type by selecting only the properties you name from an existing type. Use it to build focused types for specific views, API responses, or function parameters.