Lesson 31 of 80
"strict": true isn't one switch — it's a bundle of independent flags, and knowing what each one actually catches is the difference between trusting the compiler and fighting it.
15 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
"strict": true in tsconfig.json looks like a single setting. It isn't — it's shorthand that flips on roughly eight independent flags at once: noImplicitAny, strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, noImplicitThis, alwaysStrict, and useUnknownInCatchVariables. Each one catches a genuinely different category of bug, and each can be individually re-disabled while strict stays on — which matters a lot when you're migrating a loosely-typed codebase and can't fix everything at once.
Treating strict as one opaque switch means you either turn it on and drown in errors, or leave it off and lose the checks that actually matter. Knowing what each flag does lets you turn strictness on incrementally, flag by flag, in the order that fixes the highest-impact bugs first.
// noImplicitAny — catches an unannotated parameter
function double(x) { // ❌ error: Parameter 'x' implicitly has an 'any' type
return x * 2;
}
// strictPropertyInitialization — catches a class field with no guaranteed value
class UserSession {
userId: string; // ❌ error: Property 'userId' has no initializer
// and is not definitely assigned in the constructor
constructor() {
// userId never gets set here
}
}
Neither of these is a syntax error — both compile fine with strict off. noImplicitAny is complaining that x's type was never stated, so TypeScript silently fell back to any, defeating the entire type system for that parameter. strictPropertyInitialization is complaining about something different: a class field declared as string that the constructor never actually assigns, meaning userId is undefined at runtime despite its type claiming otherwise.
strict: true is a bundle, not an atom. You can write "strict": true and then "strictNullChecks": false right below it in the same tsconfig.json — the individual flag wins. This is exactly how large legacy codebases migrate: turn strict on, then re-disable the flags that produce too many errors to fix today, and knock them out one at a time.strictNullChecks is the highest-impact flag of the bunch. Without it, null and undefined are silently assignable to every type — a string parameter can secretly be null and nothing catches it until it crashes at runtime. Most of what people mean by "TypeScript actually caught a bug" traces back to this one flag.strictPropertyInitialization only looks at classes, and only fires when a field has neither an inline initializer (userId = "") nor a constructor assignment. It has no opinion about interfaces or plain object types — those are checked structurally regardless of this flag.noImplicitAny doesn't ban any. Writing x: any explicitly is still completely legal with this flag on. It only blocks the accidental, unannotated fallback to any — the case where you never meant to lose type safety but did anyway.Being able to name what strict actually turns on, and describe a real incremental-adoption plan for a legacy codebase, is a much stronger signal than just saying "always use strict mode." The strictNullChecks flag in particular connects directly to the non-null assertion operator covered elsewhere in this track — that operator only exists because this flag makes null/undefined visible to the type checker in the first place.