TypeScript Strict Mode in Mid-2026 — A Working Adoption Note for Existing Codebases
TypeScript strict mode has been the recommended setting for new TypeScript projects for several years. In 2026 the question that more development teams are working through is how to migrate existing non-strict codebases to strict mode without breaking the working features in the process. A working note on the migration approach.
What strict mode actually changes:
The strict flag in tsconfig.json enables a bundle of related checks. The main ones are:
noImplicitAny — disallows variables, parameters, and return types where the type cannot be inferred to default to any.
strictNullChecks — requires explicit handling of null and undefined. A variable of type string cannot be null or undefined without being typed as string | null or string | undefined.
strictFunctionTypes — checks function parameter types more strictly, catching some common errors with function-type assignments.
strictBindCallApply — type-checks the .bind(), .call(), and .apply() methods more strictly.
strictPropertyInitialization — requires that class properties are either initialised in the constructor or marked as optional or as definitely-assigned.
noImplicitThis — disallows this references where the type of this cannot be inferred.
alwaysStrict — ensures the output uses strict mode JavaScript.
In a strict TypeScript codebase all of these checks are enabled and the type system catches a large class of common errors at compile time rather than at runtime. The trade-off is that the type system requires more explicit typing work from the developer.
The migration problem:
A large existing codebase that was developed without strict mode often has thousands of type errors when strict mode is enabled. The most common categories:
Implicit any. Functions whose parameter types were never declared and were inferred as any. The error in strict mode is Parameter 'x' implicitly has an 'any' type.
Null/undefined handling. Code that accesses properties or calls methods on values that might be null or undefined. The error is Object is possibly 'null'. or Object is possibly 'undefined'.
Unfilled object properties. Classes that declare properties but do not initialise them in the constructor and do not mark them as optional. The error is Property 'x' has no initializer and is not definitely assigned in the constructor.
The default reaction to seeing thousands of errors is to give up. The correct reaction is to migrate gradually rather than enable the full strict mode at once.
A working migration approach:
Step one — enable the strict checks one at a time. Rather than enabling strict: true and confronting all errors at once, enable each strict check individually starting with the lowest-impact one.
Typical order:
alwaysStrict — generally no effect on existing well-formed TypeScript.
noImplicitThis — usually a small number of issues to fix.
noImplicitAny — addresses the broadest type-error category but each error is often easy to fix individually.
strictBindCallApply and strictFunctionTypes — usually a moderate number of issues.
strictNullChecks — typically the largest category of errors and the most demanding migration step.
strictPropertyInitialization — typically catches a moderate number of class definition issues.
Step two — use file-level overrides for the harder modules. The // @ts-nocheck directive at the top of a file disables type-checking for that file. The // @ts-expect-error directive on a single line acknowledges an error that you accept temporarily. These directives let you enable the strict check globally while excluding specific files from the check until you migrate them.
The pattern that works well is to enable strict checks globally, mark the most difficult files with @ts-nocheck to keep the build green, then work through the marked files one at a time as part of normal development work.
Step three — use the type narrowing tools that TypeScript provides for null handling. TypeScript has matured significantly through 2024–25 in the type narrowing capabilities. The if (x !== null && x !== undefined) pattern, the optional chaining operator ?., the nullish coalescing operator ??, and the discriminated union pattern all give explicit ways to handle nullable types cleanly.
Step four — establish strict-mode-from-day-one for new code. While migration of existing code is in progress, enforce strict mode for any new module added to the codebase. This prevents the migration debt from growing while the existing debt is being paid down.
Step five — set up tooling to track progress. The number of files with @ts-nocheck, the number of @ts-expect-error directives, and the total error count if strict checks are temporarily enabled are all useful metrics for tracking migration progress. A simple script that counts these and posts to the team chat each week keeps the migration visible.
Common patterns in the migration work:
The null-check spread. Many functions accept parameters that “should never be null” but are typed as nullable from the upstream type. The fix is either to type the parameter as the non-null version and check at the call site, or to handle the null case inside the function explicitly. Both patterns work and the choice is contextual.
The optional property pattern. Many object types have properties that are sometimes set and sometimes not. The fix is to mark these as optional with ? in the type definition rather than declaring them as required and leaving them undefined.
The discriminated union pattern. State machines and request/response types often benefit from discriminated unions where a discriminator field determines which other fields are present. TypeScript handles these patterns well in strict mode but the type definitions require some discipline.
The type guard pattern. For functions that check whether a value is of a particular type, TypeScript type guards (function isFoo(x: unknown): x is Foo {...}) let you express the check in a way that the compiler can use for type narrowing.
The library type fixes. Some older JavaScript libraries have incomplete or incorrect TypeScript type definitions. The fix is often to update to a newer version of the library that has improved types, or to override the types in a local declaration file.
What is realistic in terms of timeline:
A small codebase (under 10,000 lines of TypeScript) can usually migrate to strict mode in 1-2 weeks of focused work.
A medium codebase (10,000 to 100,000 lines) typically takes 1-3 months of background work alongside normal development.
A large codebase (over 100,000 lines) typically takes 6-12 months and requires explicit team commitment and tracking.
For development teams running existing TypeScript codebases in 2026, the migration to strict mode is worth doing. The error-catching at compile time pays back consistently in fewer runtime bugs, faster debugging, and easier refactoring. The migration cost is real but the work is mostly mechanical and can be done in the background of normal development.
The teams that have done the migration report that they cannot imagine going back. The type system catches errors that would otherwise have been runtime bugs and the productivity gain is meaningful once the team adapts to working in strict mode.
For new TypeScript projects starting in 2026, enabling strict mode from the start is the easy choice. The cost of starting with strict mode is much smaller than the cost of migrating later.