Dev.to
TypeScript 6.0 launches strict mode by default and drops ES5 support
TypeScript 6.0 breaking changes are not theoretical. With strict mode now default, ES5 dropped, and ESNext module resolution required, this is the most disruptive TypeScript release since 2.x. Migration is not optional: if you rely on downstream dependencies, your next npm install could break CI unless you’re ready. The break rate is not subtle — 70% of codebases that dodged strict mode now face instant red builds. Here’s what’s changed, why, and how to actually migrate without a week of pipeline outages.
What are the key breaking changes in TypeScript 6.0?
TypeScript 6.0 introduces three headline breaking changes:
Strict mode defaults to true
Previously, TypeScript shipped with strict: false unless you opted in. This masked a huge class of unsound code, but “worked” for legacy projects. That’s over. In 6.0, strict: true is enforced unless you explicitly override it.
ES5 (and ES3) dropped as valid targets
Your tsconfig.json can no longer target es5 or es3. The new default? es2025. Annual bumps are now expected; only modern browsers/environments are first-class.
Module resolution defaults to esnext
The days of defaulting to CommonJS are done. Module resolution now targets esnext, aligning with ESM-first patterns in Node.js and native ESM in browsers.
Here’s how these changes break in practice:
Strict mode: The Medium article confirms ~70% of projects break compiler checks with the new default, especially those that never explicitly enabled strict mode.
ES5: If your deployment targets Internet Explorer or other legacy browsers, your only path is a separate transpile; TypeScript itself won’t do it.
Module resolution: Node/CommonJS interop relying on implicit defaults will fail without explicit config.
The summary: TypeScript 6.0 breaks compatibility by design—to force codebase modernization now, before the Go-native 7.0 rewrite.
[[COMPARE: strict mode off vs strict mode on]]
Why did TypeScript 6.0 drop ES5 and what does it mean?
The rationale for dropping ES5/ES3 is blunt: legacy browser support is a tax on innovation that few projects need, and the ecosystem is moving on.
Legacy browsers unsupported: With Internet Explorer now universally deprecated and major browsers auto-updating, supporting ES5-level output is overhead only useful for a sliver of enterprise edge-cases.
Benefits of ES2025+ targets: Targeting modern ECMAScript versions (es2025 and future annual updates) enables smaller, faster, and more predictable output. Using newer syntax enables dead code elimination, improved minification, better async semantics, and native module loading in most environments.
Migration impact: If you maintain a web app for environments that require ES5—think corporate thin clients or fossilized embedded browsers—TypeScript is no longer your transpiler. You’ll need to swap in an additional Babel step or similar tooling. For most teams, this change means nothing but simpler config.
The removal is explicit: there is no flag to opt back in. Any target: es5 or target: es3 setting in your tsconfig.json will throw an error on boot. This is not “just” a migration—languages rarely yank platform support this decisively. TypeScript 6.0 does.
Takeaway: Review your browser support matrix now. If it includes any runtime that can’t parse ES2025, you need a new transpilation pipeline—not just a tsconfig tweak.
How does enabling strict mode by default affect your codebase?
Strict mode in TypeScript is not a single flag; it’s an umbrella for eight distinct compiler checks. The 6.0 release flips the default to strict: true for everyone.
What “strict mode on” actually enables:
strictNullChecks
strictFunctionTypes
strictBindCallApply
strictPropertyInitialization
noImplicitAny
noImplicitThis
alwaysStrict
useUnknownInCatchVariables
Prior to 6.0, unless you opted in, every one of these checks was off by default. Now, every unchecked edge—implicit any, missing property initialization, unsound function calls—surfaces as a red CI build.
The impact is nontrivial:
Per the Medium article, about 70% of all codebases that never set strict: true will break on the spot. This means every loosely-typed model, every third-party package relying on legacy config, every test util built with shortcuts will fail.
Typical break patterns:
Uninitialized class properties
Functions with implicit any parameters
Unchecked null/undefined accesses
Implicit type conversions in function arguments
Your logs will read like:
// Before (compiles in TS ≤5.x)
function foo(bar) { return bar.toString(); }
// After (TS 6.0 strict mode default)
error TS7006: Parameter 'bar' implicitly has an 'any' type.
And:
class User {
id: number;
// error: Property 'id' has no initializer and is not definitely assigned in the constructor.
}
Workarounds like // @ts-ignore are not sustainable—CI will be red until you fix actual typing errors or rewrite unsound branches.
Takeaway: audit for any missing strict setting. Your pain is directly proportional to how much you avoided this flag in the past.
What are the changes to module resolution in TypeScript 6.0?
The third major breaking change: module resolution default has shifted from commonjs to esnext.
What this means:
If you omit module in your tsconfig.json, TypeScript will now output ESNext syntax—native JavaScript modules.
CommonJS interop is no longer assumed; you must set module: 'commonjs' explicitly if you target Node CJS environments.
ESM syntax (import/export) is now canonical.
Typical errors you might see:
Import/export mismatches (“Cannot use import statement outside a module”)
Default import ambiguity (differences in how import foo from 'bar' resolves)
Node.js projects with ESM/CJS edge cases (especially if you rely on dynamic requires or legacy loaders)
A minimal config impact:
// Before (TS ≤5.x, default is 'commonjs')
{
"compilerOptions": {
"target": "es2020",
// module not set = commonjs
}
}
// After (TS 6.0, default is 'esnext')
{
"compilerOptions": {
"target": "es2025"
// module not set = esnext
}
}
Takeaway: explicit is safe—always set module in tsconfig.json if your environment relies on anything but pure ESM.
How to migrate your production codebase to TypeScript 6.0 without breaking CI
A careless yarn upgrade typescript is the path to a lost week. Migration needs discipline—stepwise, reversible, test-driven.
Step 1: Audit your config
Look for implicit defaults:
// Dangerous: no strict or explicit target/module
{
"compilerOptions": {
//...
}
}
Add explicit strict, module, and target settings before upgrading:
{
"compilerOptions": {
"strict": false, // Or true, if you’re already compliant
"target": "es2020", // Your current build target
"module": "commonjs" // Or your actual runtime
}
}
Step 2: Lock TypeScript version in CI
In your pipeline config, freeze the TypeScript version before bumping:
# package.json
"devDependencies": {
"typescript": "5.4.3"
}
Pin in CI:
# .github/workflows/ci.yml
- run: npm ci
Test the current build under the old version. Only then try 6.0 on a feature branch.
Step 3: Upgrade TypeScript and tsconfig — in a branch
In a feature branch:
npm install --save-dev typescript@6.0.0
Update tsconfig.json:
Remove any now-invalid target: "es5" or "es3"
Set target: "es2025" (or as high as your runtime supports)
Set strict: true where possible; otherwise, add type fixes incrementally
Explicitly set module (e.g., "commonjs" or "esnext")
A minimal compliant config:
{
"compilerOptions": {
"strict": true,
"target": "es2025",
"module": "esnext"
}
}
Step 4: Refactor for strict mode
Run the compiler:
npx tsc --noEmit
Fix all errors—especially those that surface with strictNullChecks, missing initializers, and implicit anys. Use explicit types, opt-in initializers, and disciplined null handling.
Step 5: Test and roll out
Run the entire test suite (jest, vitest, or your stack-of-choice). Add/expand coverage across models and utils. TypeScript can only catch what’s declared; your tests catch the rest.
Merge only when CI is green and run in canary/prod-shadow mode as needed.
Step 6: Communicate downstream
If you maintain libraries, bump your peer dependency to ^6.0.0 and note the breaking strict mode and module resolution in the release notes.
Takeaway: Each step is atomic—no silent breakage, no upstream surprises. Migration is a process, not a flag flip.
[[DIAGRAM: the migration flow — from audit to CI-green with TypeScript 6.0]]
What to expect in TypeScript 7.0 and why 6.0 is the last legacy version
TypeScript 6.0 is not just another major. Per the Medium article, this is the final release before a planned rewrite: TypeScript 7.0 will switch to a Go-native core. That means:
Compatibility layers for old targets, patchwork Node quirks, and edge-case flags will be gone.
6.0 is the last station for “legacy-friendly” settings. If you’re not up to date, a 7.0 upgrade will be vastly harder.
By migrating now, you rationalize types and modernize your runtime. The next jump will be smoother, not catastrophic.
Takeaway: treat 6.0 as the prep round before the TypeScript 7.0 rewrite wave hits. Deferred upgrades stop being feasible with the next release.
Migrating now is the only way to avoid weeks of CI outages later
TypeScript 6.0 breaking changes—strict mode forced on, ES5 dropped, and module resolution defaulting to ESNext—are not footnotes. They affect every codebase that leans on legacy assumptions. Proactive migration, with explicit config, incremental type hardening, and careful CI rollout, is the only solid path through. 7.0 will not wait. Do the hard work now and future upgrades become routine, not a crisis.
3 hours ago