TypeScript strictness
TypeScript's value is proportional to how strict you let it be. A loose
config with implicit any everywhere is JavaScript with extra syntax;
strict mode is where the compiler starts catching the null-deref and
type-mismatch bugs before they ship.
Method
- Turn on
strictfor new projects, full stop. It enables the flags that matter together (strictNullChecks,noImplicitAny,strictFunctionTypes, and the rest). Everything below is about getting an existing loose codebase there without a rewrite. - Migrate flag by flag, not all at once. Enabling
stricton a large loose codebase surfaces thousands of errors and stalls. Turn on one flag at a time, fix its errors, commit, repeat.noImplicitAnyandstrictNullChecksare the two that find the most bugs and cause the most work; schedule them deliberately. - Add
strictNullChecksearly and take it seriously. It separatesTfromT | null | undefined, forcing you to handle absence at every boundary. This is the single flag that prevents the most runtime errors (the "cannot read property of undefined" class; see null-handling). Expect it to touch a lot of code; the errors are real bugs, not noise. - Reach for
noUncheckedIndexedAccessonce strict is stable. It makesarr[i]returnT | undefined, catching the off-by-one and missing-key access thatstrictalone misses. High value, moderate friction; add it after the baseline is clean. - Contain the migration debt visibly. Where you must defer fixes,
use explicit
// @ts-expect-errorwith a reason (it fails the build when the underlying issue is fixed, unlike@ts-ignorewhich rots silently), and track the count downward. Never widen types toanyto silence errors; that spreads the looseness (see ts-api-types). - Enforce in CI and forbid backsliding.
tsc --noEmitin CI (see linting-setup); lint rules againstanyand non-null assertions (!) except where justified. A strict config that any PR can quietly loosen is not strict.
Boundaries
- Strictness catches type bugs, not logic bugs; a fully-typed program can still be wrong (see testing-strategy).
- Third-party libraries with weak or wrong types leak looseness in; isolate them behind typed wrappers at the boundary rather than weakening your own config (see ts-api-types).
strictis a floor, not the ceiling:noUncheckedIndexedAccess,exactOptionalPropertyTypes, and lint rules add more, each with its own friction-to-value tradeoff to weigh per project.