tsconfig strict: Turn It On Without 200 Errors
Count what each strict flag costs, turn them on one at a time, park one stubborn error with ts-expect-error, and finish with strict true. After this, an old project can move to strict in steps.
Turning on "strict": true in an old project can print 200 errors in one go. You do not have to fix them in one go. This page shows how to turn strict mode on in steps, count what each step costs, and finish with strict on and nothing switched off.
What strict is made of
strict is a bundle of separate checks. The two that produce most of the errors in a JavaScript-to-TypeScript migration are:
noImplicitAny: parameters and variables that would silently beany.strictNullChecks: places wherenullorundefinedcan reach code that expects a value.
A third one, useUnknownInCatchVariables, makes the e in catch (e) an unknown, so e.message needs a check first.
The sample file
This old code is typical. It compiles with strict mode off.
{
"compilerOptions": {
"target": "es2022",
"module": "nodenext",
"strict": false,
"noEmit": true
},
"include": ["src"]
}
function discount(price, rate) {
return price - price * rate;
}
let customer: string = null;
function firstLong(items: string[]) {
return items.find((item) => item.length > 3);
}
console.log(firstLong(["ab", "abcd"]).toUpperCase());
try {
JSON.parse("{");
} catch (e) {
console.log(e.message);
}
export { discount, customer };
npx tsc
No output means no errors. That is your baseline.
Step 1: count what each flag would cost
Turn on one flag at a time from the command line, without editing the config. The count tells you where to start. The grep -c counting works on macOS and Linux. On Windows, drop the | grep part and count the lines by eye.
npx tsc --noImplicitAny | grep -c "error TS"
npx tsc --strictNullChecks | grep -c "error TS"
npx tsc --useUnknownInCatchVariables | grep -c "error TS"
npx tsc --strict | grep -c "error TS"
2
2
1
5
Four numbers. Two come from noImplicitAny, two from strictNullChecks, one from the catch, and strict shows the total. On a real project the numbers are bigger, but the idea is the same: you can see which flag is the expensive one before you commit to it.
Step 2: switch on the cheap ones first
Edit tsconfig.json. Start with the mechanical flag and leave the hard one off for now.
{
"compilerOptions": {
"target": "es2022",
"module": "nodenext",
"strict": true,
"strictNullChecks": false,
"noEmit": true
},
"include": ["src"]
}
npx tsc
src/legacy.ts(1,19): error TS7006: Parameter 'price' implicitly has an 'any' type.
src/legacy.ts(1,26): error TS7006: Parameter 'rate' implicitly has an 'any' type.
src/legacy.ts(16,17): error TS2339: Property 'message' does not exist on type 'unknown'.
"strict": true turns everything on, and then "strictNullChecks": false turns that one flag back off. That pattern is the useful part. You get the bundle's other checks now, and the hard flag is one line to remove later. Fix the errors that show up, then run tsc again. For this file, that means two number types and a check in the catch.
function discount(price: number, rate: number): number {
return price - price * rate;
}
try {
JSON.parse("{");
} catch (e) {
console.log(e instanceof Error ? e.message : e);
}
export { discount };
Step 3: park a stubborn error on purpose
If one line needs more time than you have, mark it so it cannot be forgotten.
// TODO: remove when the customer lookup is typed.
// @ts-expect-error
let customer: string = null;
export { customer };
The comment silences the error on the next line only. The useful part is what happens once you fix the line.
// @ts-expect-error
let customer: string = "Ana";
export { customer };
src/fixed.ts(1,1): error TS2578: Unused '@ts-expect-error' directive.
When the error is gone, the directive itself becomes an error, so the comment cannot hide in your code after the problem is solved. // @ts-ignore does not do that, which is why this one is the better choice.
Step 4: finish
Remove "strictNullChecks": false from the config, fix what appears, and you are on full strict with a clean compile. The final config has "strict": true and nothing after it.
For a very large project
If fixing a whole project is too much at once, keep the old config and add a second one that is strict and covers only the folders you have already cleaned.
{
"extends": "./tsconfig.json",
"compilerOptions": { "strict": true, "strictNullChecks": true },
"include": ["src/clean"]
}
The second flag matters here. Your main config still has "strictNullChecks": false from step 2, and a setting written in the main file stays in force unless the new file overrides it. Run npx tsc -p tsconfig.strict.json on the build server next to the normal compile. As each folder is fixed, add it to include. When the list is everything, delete the second file and set strict in the main one.
⚠ Warning: Do not leave the migration at "strict: false with a TODO". Without a date or a ticket, it will not happen.
Related course: TypeScript for Beginners.
About
Count what each strict flag costs, turn them on one at a time, park one stubborn error with ts-expect-error, and finish with strict true. After this, an old project can move to strict in steps.
- Responsible
- Mary Q
- Last Update
- 10/19/2026
- Reading Time
- 5 min
Reviews
Sign in to write a reviewComments (0)
Sign in to commentNo comments yet.