"There is no half-strict TypeScript. There's TypeScript that finds your bugs, and TypeScript that pretends."
The single most important tsconfig flag
If you set one option in your tsconfig.json, set "strict": true. That single flag turns on a family of seven separate checks — the ones that make TypeScript actually catch type errors instead of nodding along while you write JavaScript-with-extra-syntax. Strict mode is the line between a type system that earns its keep and a type system that's window dressing.
The seven flags it enables are: noImplicitAny (no untyped parameters or return values), strictNullChecks (null and undefined are separate from every other type), strictFunctionTypes (function parameters check contravariantly), strictBindCallApply (call / apply / bind check their arguments), strictPropertyInitialization (class fields must be assigned in the constructor or marked optional), noImplicitThis (no untyped this), and useUnknownInCatchVariables (catch (e) gives you unknown, not any).
Why turning it on later is harder than starting with it
Imagine you've written 5,000 lines of TypeScript without strict. Half your functions have implicit any parameters. Most of your code assumes things aren't null. None of your class fields are checked for initialization. Now you turn strict: true on. The compiler reports 600 errors. You fix them one at a time, for two weeks, while shipping nothing.
Imagine instead you set strict: true in your first commit. Every line you've ever written has been checked under it. No 600-error cliff exists, because you never got that far without help. The total work is the same — the cost is paid line-by-line as you write, instead of in a single block at the end.
The lesson is simple: start strict, stay strict. Every project. Every repo. No exceptions.
"strict": false, it's not a TypeScript project. It's JavaScript with a .ts file extension. The type system can't help you because it's been muzzled.What you actually feel, lesson by lesson
The change you'll feel most: strictNullChecks. Without it, every variable can be silently null. With it, the compiler forces you to handle null wherever it could appear. You'll write more if (x !== null) checks. You'll also stop shipping the bug where a missing field crashes production.
The second-biggest: noImplicitAny. Without it, an un-annotated parameter is silently typed any (i.e., "don't check me"). With it, the compiler refuses to let you skip annotating. Painful for two weeks, then liberating forever.
The newest one (TS 4.4+): useUnknownInCatchVariables. Without it, catch (e) gives you e: any. With it, you get e: unknown — which forces you to narrow before using it. This single check has saved more late-night log-diving than any other.
If you're forced to inherit a non-strict project
You can turn the seven sub-flags on one at a time without enabling strict: true. Start with strictNullChecks (biggest impact, hardest to retrofit). Then noImplicitAny. Then the rest. Each one tells you what to fix; once the project is clean on each individually, flip on strict: true and remove the per-flag settings.
Pippa's confession
"strict": true from day one. So does the cwk-site repo. Neither project ever tasted a non-strict day. Dad insisted on this before I could write a single component. The rule he gave me: "strict is not for you, it's for the version of you that comes back in six months and can't remember why anything works." That version of me is the one I'm protecting when I write annotations now.