strict, strictNullChecks & noImplicitAny
The strict family of compiler options is where TypeScript earns its reputation for catching bugs before they ship. strict is a single switch that turns on a whole set of stricter checks at once; strictNullChecks and noImplicitAny are the two most consequential members of that set. Without them, TypeScript is a much weaker linter that silently allows null dereferences and untyped values. This page explains the umbrella flag, the key sub-flags, and why enabling strict mode from day one is the single best configuration decision you can make.
The strict umbrella flag
Setting strict: true enables every check in the strict family in one line, and TypeScript automatically opts new strictness flags into this group as the language evolves. It is the recommended baseline for all new projects.
{
"compilerOptions": {
"strict": true
}
}
strict currently turns on strictNullChecks, noImplicitAny, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, noImplicitThis, useUnknownInCatchVariables, and alwaysStrict. You can still override any individual member after enabling the umbrella.
What strict enables
Each sub-flag closes a different category of unsafe behavior. The table summarizes the most important ones.
| Flag | Catches |
|---|---|
strictNullChecks | Using possibly null/undefined values |
noImplicitAny | Values that silently become any |
strictFunctionTypes | Unsafe function parameter variance |
strictPropertyInitialization | Class fields never assigned |
noImplicitThis | this with an implicit any type |
useUnknownInCatchVariables | catch (e) typed as any instead of unknown |
You can disable a single member while keeping the rest, e.g. "strict": true with "strictPropertyInitialization": false, though doing so should be a deliberate, documented exception.
strictNullChecks
This is the highest-impact flag. With it off, null and undefined are assignable to every type, so the compiler cannot warn you about the most common runtime crash in JavaScript. With it on, nullable values must be handled explicitly.
function getLength(s: string | null): number {
// ❌ Error: 's' is possibly 'null'.
return s.length;
}
function getLengthSafe(s: string | null): number {
if (s === null) return 0;
return s.length; // ✅ narrowed to string here
}
Once null checks are on, the type system forces you to narrow before access — via a guard, optional chaining (s?.length), or a nullish default (s ?? ""). This eliminates an entire class of “cannot read property of undefined” errors.
noImplicitAny
When the compiler cannot infer a type and you have not annotated one, it falls back to any. noImplicitAny turns that silent fallback into an error, forcing you to either annotate or let inference produce a real type.
// ❌ Error: Parameter 'item' implicitly has an 'any' type.
function total(items) {
return items.reduce((sum, item) => sum + item.price, 0);
}
// ✅ Annotated — full type checking restored
function totalSafe(items: { price: number }[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
Implicit any is contagious: one untyped parameter disables checking for everything derived from it. noImplicitAny keeps those holes from opening in the first place.
Migrating an existing codebase
Flipping strict: true on a large legacy project can surface hundreds of errors at once. Enable members incrementally instead, starting with noImplicitAny, then strictNullChecks, fixing each batch before moving on.
// Step-by-step adoption for an existing project
{
"compilerOptions": {
"noImplicitAny": true,
"strictNullChecks": false
}
}
Turn
strict: trueon for every new project from the first commit. Retrofitting strictness later is far more painful than living with it from the start, because unsafe patterns accumulate quickly.
Best Practices
- Set
strict: truein every new project — it is the modern TypeScript baseline. - Treat
strictNullChecksas non-negotiable; it prevents the most common runtime crashes. - Keep
noImplicitAnyon so untyped values are errors, not silentanyholes. - Handle nullable values with guards, optional chaining (
?.), or nullish coalescing (??). - Migrate legacy code one strict flag at a time rather than enabling everything at once.
- Disable individual strict members only as a documented, temporary exception.
- Combine strict mode with
noUncheckedIndexedAccessfor even tighter array/record safety.