Overview
Enabling strict on an existing codebase surfaces errors that were silently passing. Enable one flag at a time and fix by error code so the work stays reviewable. The flag reference is in typescript-strict-mode; every tsconfig option is in typescript-tsconfig. Starting with TypeScript 6.0 (March 2026), strict defaults to true, so upgrading the compiler without setting it explicitly can surface these errors on its own; TypeScript 7.0 (July 2026) is the native-compiler release.
Prerequisites
npx tsc --versionworks. Any version works; on 6.0 and later the steps apply to projects that set"strict": false, or that inherit it from an older config.- The build and tests pass before you start.
Steps
1. Measure the baseline
npx tsc --noEmit 2>&1 | tee tsc-errors.txt
grep -o 'error TS[0-9]*' tsc-errors.txt | sort | uniq -c | sort -rn | head2. Enable flags in this order
Set "strict": false and turn the family on one flag at a time, fixing each batch before the next:
noImplicitAny: add explicit parameter and return types. Useunknownplus narrowing when the type is genuinely open (typescript-narrowing); avoidany.strictNullChecks: guard instead of asserting.strictFunctionTypes,strictBindCallApply,strictPropertyInitialization,noImplicitThis,useUnknownInCatchVariables,alwaysStrict.- Replace the individual flags with
"strict": true.
const user = getUser();
if (!user) throw new Error("User not found"); // a guard, not user!.name
console.log(user.name);Use the non-null assertion ! only when you know something the compiler cannot, and comment why.
3. Fix the common codes
- TS7006, TS7031: implicit
anyparameter or binding. Annotate it. - TS2532, TS18048: possibly
undefined. Guard or default it. - TS2564: property not initialized. Initialize it in the constructor; use
!only when a framework assigns it. - TS18046:
catch (e)isunknown. Narrow withe instanceof Error.
4. Add noUncheckedIndexedAccess
It is not part of strict, and it makes items[0] typed Item | undefined, which catches real out-of-range crashes.
{ "compilerOptions": { "strict": true, "noUncheckedIndexedAccess": true } }const first = items[0];
if (first === undefined) return;5. Gate CI
Add npx tsc --noEmit to the pipeline so strictness cannot regress.
Verify it worked
npx tsc --noEmit && echo clean
npx tsc --noEmit 2>&1 | grep -c "error TS" # 0
npm testCommon errors
- Hundreds of errors at once. Enable flags singly, as in step 2.
- A library has no types. Install
@types/<package>, or declare the module; avoidskipLibCheckas a fix for your own errors. !spreads across the code. Each one hides a nullable path; replace them with guards or restructure so null cannot occur.strictPropertyInitializationbreaks a DI framework that assigns properties after construction. Use!on those properties, with a comment.- Behavior changes after narrowing. Review code that narrows with
typeof,instanceof, or equality checks.
FAQ
Is strict mode on by default in TypeScript?
Yes, starting with TypeScript 6.0 (March 2026); upgrading the compiler without setting strict explicitly can surface strict errors on its own.
How do I turn on strict mode without hundreds of errors at once?
Set "strict": false and enable the family one flag at a time, starting with noImplicitAny and then strictNullChecks, fixing each batch before the next.
Does strict include noUncheckedIndexedAccess?
No. Add it separately; it types items[0] as Item | undefined, which catches real out-of-range crashes.
When is the non-null assertion operator acceptable?
Only when you know something the compiler cannot, such as a DI framework assigning a property after construction, and with a comment saying why.
How do I stop strictness from regressing?
Add npx tsc --noEmit to the CI pipeline.