TypeScript · Tooling & practice
`strict` mode & flags
⚡ TL;DR —
strict: trueis not one flag, it's an umbrella that turns on ~8 independent checks — the important ones beingstrictNullChecks(null/undefined stop being assignable to everything) andnoImplicitAny(untyped values error instead of silently becomingany). Without strict, TypeScript is a linter that lies to you; with it, the types actually mean something.
🧠 Mental model
Think of strict as the line between "TypeScript that catches bugs" and "TypeScript as expensive syntax highlighting." The single most valuable member is strictNullChecks: with it off, null and undefined inhabit every type, so user.name.toUpperCase() compiles even when user can be null — and then crashes. With it on, null is its own thing you must narrow away, and the compiler forces you to handle the empty case. The billion-dollar mistake, fixed at the type level.
strict is a meta-flag: setting it flips a family of sub-flags to true. You can then selectively opt out of individual ones (strict: true + "strictNullChecks": false), which is exactly how you stage a migration.
⚙️ How it actually works
strict: true enables (as of current TS) this family:
| Flag | What it forbids |
|---|---|
strictNullChecks |
Treating null/undefined as members of other types |
noImplicitAny |
Values whose type can't be inferred silently becoming any |
strictFunctionTypes |
Unsound contravariant function-parameter assignments |
strictBindCallApply |
Wrongly-typed .bind/.call/.apply arguments |
strictPropertyInitialization |
Class fields not assigned in the constructor (needs strictNullChecks) |
noImplicitThis |
this of implicit any type |
useUnknownInCatchVariables |
catch (e) giving e: any instead of unknown |
alwaysStrict |
Emitting without JS "use strict" |
Crucially, strict is erased at runtime — like all of TypeScript, it changes nothing in the emitted JS except alwaysStrict's "use strict" pragma. It's purely a compile-time contract.
Flags people wrongly assume are part of strict but aren't: noUncheckedIndexedAccess (makes arr[i] return T | undefined — arguably the most valuable non-strict flag), exactOptionalPropertyTypes, noImplicitReturns, noFallthroughCasesInSwitch, and noImplicitOverride. These are opt-in on top of strict.
💻 Code
// tsconfig.json — the baseline every new project should ship
{
"compilerOptions": {
"strict": true,
// Not in `strict`, but you almost always want them:
"noUncheckedIndexedAccess": true, // arr[i] is T | undefined — forces bounds handling
"noImplicitOverride": true, // must write `override` when overriding
"noFallthroughCasesInSwitch": true
}
}
// strictNullChecks OFF → this compiles and crashes at runtime:
function greet(name: string) { return name.toUpperCase(); }
greet(null); // ❌ no error without the flag; 💥 TypeError at runtime
// strictNullChecks ON → the compiler forces the guard:
function greetSafe(name: string | null) {
if (name == null) return "hi"; // ✅ must narrow first
return name.toUpperCase();
}
// noUncheckedIndexedAccess ON → the off-by-one the type system now catches:
const xs = [1, 2, 3];
const first = xs[0]; // type is number | undefined, not number
first.toFixed(); // ❌ error until you handle undefined
⚖️ Trade-offs
- Turn
stricton from day one on greenfield. It's near-free early and brutally expensive to retrofit later, because every unhandled null is a latent bug your team has been ignoring. - On a legacy codebase, migrate incrementally. Set
strict: truethen disable the loudest sub-flag (usuallystrictNullChecks), fix the rest, and re-enable it directory-by-directory. Don't leavestrict: false— you lose the cheap wins (noImplicitThis,strictBindCallApply) for no reason. noUncheckedIndexedAccessis high-value but noisy. It's correct — array/record access can be undefined — but it forces guards on every lookup. Teams that lean heavily onmap[key]sometimes find the friction not worth it; know it's a deliberate call, not an oversight.- When NOT to obsess: flags like
exactOptionalPropertyTypescatch a narrow class of bug ({ x: undefined }vs{}) and can generate churn disproportionate to value on some codebases.
💣 Gotchas interviewers probe
- "Is
stricta single flag?" No — it's an umbrella of ~8. Naming even three or four sub-flags signals you actually configure projects rather than copy a tsconfig. strictdoes nothing at runtime. A candidate who thinks strict mode adds runtime checks doesn't understand type erasure. The only emit difference isalwaysStrict's"use strict".strictPropertyInitializationdepends onstrictNullChecks. Disabling the latter silently disables the former — a common "why isn't this erroring?" moment.noUncheckedIndexedAccessis NOT instrict. Many assume it is. It's separately opt-in, and it's arguably the flag that catches the most real bugs afterstrictNullChecks.useUnknownInCatchVariablesmakescatch (e)giveunknown, notany. People writinge.messagedirectly break under strict and must narrow (e instanceof Error).- Adding flags is a code change, not just config. Flipping a flag can surface hundreds of errors — it's a migration, and you scope it like one.
🎯 Say this in the interview
"
strictis a meta-flag — it switches on about eight independent checks. The one that matters most isstrictNullChecks: without it,nullandundefinedare assignable to every type, so the compiler happily lets you deref something that can be null and it crashes at runtime. With it on, null is its own type you have to narrow away.noImplicitAnyis the other big one — it stops values silently degrading toany. I turnstricton from the first commit on new projects because retrofitting it later means auditing every latent null bug at once. On top of strict I usually addnoUncheckedIndexedAccessso array indexing returnsT | undefined, which is technically correct and catches real off-by-ones. And I'd flag that none of this exists at runtime — it's all erased; the only emit change is theuse strictpragma."
🔗 Go deeper
- TSConfig reference — strict — the authoritative list of what the umbrella enables.
- TSConfig — noUncheckedIndexedAccess — the high-value flag that isn't in strict.
- TS 2.0 — strictNullChecks — the origin and rationale of null-safety.
- Total TypeScript — tsconfig cheat sheet — a pragmatic, opinionated baseline config.