When to use
Use when the user has an existing JavaScript project and wants to land
TypeScript without freezing feature work. The proven pattern is to add
TypeScript as a parallel type-checker first (allowJs: true,
noEmit: true), then progressively enable the strict flags one cohort
at a time. Each flag surfaces a different category of latent bug, so
flipping them all at once produces an unmergeable diff.
The flags to enable, in the order the TypeScript team itself recommends:
noImplicitAny— implicitanyis the most common escape hatch in hand-written JS and the biggest source of late-discovered bugs.strictNullChecks— forces every reference to be narrowed before use; this is the single biggest reduction in runtimeTypeErrors.strictFunctionTypes,strictBindCallApply,strictPropertyInitializationalwaysStrict— emits"use strict"in the output and tightens parser semantics.noImplicitThis,useUnknownInCatchVariables
Examples
For example, a minimal tsconfig.json that type-checks a JS tree without
emitting anything:
{
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"noEmit": true,
"target": "ES2022",
"module": "ESNext",
"strict": false
},
"include": ["src/**/*"]
}
Then, after the first cohort of noImplicitAny errors has been fixed,
tighten the config and pin the new flag in CI:
{
"compilerOptions": { "strict": true, "noImplicitAny": true }
}
A before / after snippet showing the most frequent migration fix:
// before — `user` is implicit any
function greet(user) {
return "hello " + user.name.toUpperCase();
}
// after — explicit shape
interface User { name: string }
function greet(user: User): string {
return `hello ${user.name.toUpperCase()}`;
}
Pitfalls to avoid
- Do not enable
strict: trueon day one; the resulting diff is too large to review and will block the migration PR for days. - Do not lean on
@ts-nocheckor@ts-ignoreto "make it pass"; those accumulate and become permanent dead weight. Use@ts-expect-errorwith a comment explaining the unfixed error instead. - Do not pin
strictNullCheckswhile leavingnoImplicitAnyoff; the two flags reinforce each other, and turning them on in isolation produces inconsistent error reports. - Do not skip the
skipLibCheck: trueflag if you depend on npm packages with imperfect typings — otherwise their errors block your own build. - Do not forget to update
tsc --buildreferences andpathsaliases; the strict checker enforces alias consistency where the loose checker silently coerces.