TypeScript 6.0 Migration — How to Handle baseUrl Removal, rootDir Fixing, and strict Errors All at Once with ts5to6
Honestly, when I first read the TypeScript 6.0 release notes, I rubbed my eyes three times. Nine tsconfig defaults changing all at once. baseUrl deprecated, strict flipped to true by default, module bumped from commonjs to esnext. But once I dug in, the two most breaking changes were handled by tooling, and the scope of what I actually had to touch was much narrower than I expected.
This article is for developers using TypeScript in production. It walks through the actual migration steps, distinguishing what the ts5to6 CLI tool — created by Andrew Branch, who works on module resolution for the TypeScript compiler team — handles automatically, versus what you need to take care of yourself.
TypeScript 7.0 Beta dropped in April 2026, and starting with 7.0, the compiler switches to a Go-based implementation. Skipping the 6.0 migration means a full rewrite awaits you at 7.0, rather than an incremental transition. The timing is perfect right now.
Core Concepts
The 9 Default Changes in TypeScript 6.0
TypeScript 6.0, officially released on March 23, 2026, is the last version of the JavaScript-based TypeScript compiler. Starting with 7.0, a completely rewritten Go-based compiler takes over. The defining characteristic of this release is that the team finally did a sweeping cleanup of long-overdue default values all in one go.
| Option | TS 5.x Default | TS 6.0 Default |
|---|---|---|
strict |
false |
true |
module |
commonjs |
esnext |
target |
es3 |
es2025 |
moduleResolution |
node10 |
bundler |
esModuleInterop |
false |
true |
rootDir |
Common directory of input files | Directory containing tsconfig.json |
types |
All @types/* |
[] (empty array) |
noUncheckedSideEffectImports |
false |
true |
libReplacement |
true |
false |
The three that caused me the most trouble were strict, rootDir, and baseUrl. The rest aren't without impact either — noUncheckedSideEffectImports in the table directly affects side-effect-only imports like import './polyfill'. If you use polyfills or CSS imports, you'll need to check those separately. libReplacement: false only affects the special case where you're swapping in custom lib files, so most projects won't feel it.
baseUrl Deprecated — It Doesn't Stop Working Immediately in 6.0
I misunderstood this at first too, so it's worth being precise. In TypeScript 6.0, baseUrl is officially deprecated and will generate a warning, but the feature itself still works. Complete removal is planned for TypeScript 7.0. The timeline is not "all your imports break right now," but rather "you get a warning now, and it's completely removed in 7.0."
What is
baseUrl? Setting"baseUrl": "./src"in tsconfig allowed absolute-style imports likeimport { foo } from 'components/Button'. Starting in 6.0, this approach is deprecated, and you need to migrate to explicitpathsconfiguration.
The reason to migrate now is simple: if baseUrl is still in place when you move to 7.0, that becomes a much bigger burden.
How the rootDir Default Change Silently Breaks Builds
This is the kind of problem with no error message that you only discover after deployment, which makes it especially annoying.
In TS 5.x, if you didn't specify rootDir, it was automatically inferred as the common parent directory of your input files. If your source was under src/, rootDir was set to src/, and the build output came out as dist/index.js. In TS 6.0, the rootDir default changes to the directory where tsconfig.json lives.
# TS 5.x (rootDir unspecified): dist/index.js
# TS 6.0 (rootDir unspecified): dist/src/index.js ← path is differentIf your deployment scripts or runtime entry points reference dist/index.js, it will silently break.
types: [] Default — Only Affects Global Ambient Types
What are
@types/*? Packages like@types/nodeand@types/reactthat provide TypeScript type definitions for JavaScript libraries.
There's an important distinction here. Changing to types: [] doesn't block all @types/* packages. It only disables ambient types that were previously included globally and automatically. Types that you import directly, like import type { Foo } from 'some-lib', are not affected.
The cases that are actually impacted are Node.js global types like process and Buffer. Previously, having @types/node installed meant it was automatically available globally. Now you need to explicitly add "types": ["node"] to your tsconfig.
Practical Application
Step 1: Automate baseUrl Removal with --fixBaseUrl
ts5to6 can be run directly with npx.
# Remove baseUrl and rewrite paths in the current directory's tsconfig.json
npx @andrewbranch/ts5to6 --fixBaseUrl .
# Specify a custom tsconfig file
npx @andrewbranch/ts5to6 --fixBaseUrl ./tsconfig.app.jsonSeeing exactly what the tool transforms makes it easier to understand.
Before (tsconfig.json):
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"components/*": ["components/*"],
"utils/*": ["utils/*"]
}
}
}After (tsconfig.json):
{
"compilerOptions": {
"paths": {
"components/*": ["./src/components/*"],
"utils/*": ["./src/utils/*"]
}
}
}If you were using baseUrl as a path prefix, ./src/ is automatically prepended to each paths entry. If you only had baseUrl set with no paths at all, a wildcard catch-all entry is also added automatically.
| Transformation | Handled by Tool |
|---|---|
Removing the baseUrl field |
Automatic |
Prepending baseUrl value to paths entries |
Automatic |
| Adding catch-all paths entry | Automatic |
paths conflicts in shared base tsconfig inheritance |
Manual intervention required |
In monorepo environments, it processes all child tsconfigs by following the project references chain and extends inheritance. It works without additional configuration in pnpm workspaces or Turborepo setups.
Step 2: Preserve Build Output Paths with --fixRootDir
# Explicitly fix rootDir to match what TS 5.x would have inferred
npx @andrewbranch/ts5to6 --fixRootDir .
# Specify a package tsconfig in a monorepo
npx @andrewbranch/ts5to6 --fixRootDir ./packages/core/tsconfig.jsonThe tool calculates the rootDir value that TS 5.x would have inferred and inserts it explicitly into your tsconfig. Your build output structure is preserved as-is.
Caution:
--fixBaseUrland--fixRootDircannot be run simultaneously. Each must be run separately, and the safe order is--fixBaseUrlfirst, then--fixRootDir. Checking the changes withgit diffafter each step makes it much easier to trace the root cause if something goes wrong later.
Step 3: Addressing the strict: true Default
With strict now defaulting to true, code that was written without type annotations will start throwing errors. Here are two patterns you'll frequently encounter in production.
// src/utils.ts — patterns that were allowed in TS 5.x
function greet(name) { // noImplicitAny — no parameter type
return `Hello ${name}`;
}
const val = maybeNull.toUpperCase(); // strictNullChecks — ignoring possible null// src/utils.ts — addressing TS 6.0 strict: true
function greet(name: string) {
return `Hello ${name}`;
}
const val = maybeNull?.toUpperCase() ?? '';If there are too many errors to fix all at once, you can use a temporary buffer strategy.
// tsconfig.json
{
"compilerOptions": {
"ignoreDeprecations": "6.0",
"strict": false
}
}This temporarily suppresses deprecation warnings and preserves the old behavior. However, since ignoreDeprecations itself is not supported in TypeScript 7.0, you can't defer the real migration indefinitely.
Step 4: Import Changes from the esModuleInterop Default Change
With esModuleInterop now defaulting to true, the import syntax for CommonJS modules changes.
// src/server.ts — TS 5.x style (esModuleInterop: false default)
import * as express from "express";// src/server.ts — TS 6.0 style (esModuleInterop: true default)
import express from "express";This change improves compatibility with Vite, esbuild, and Rollup-based projects. For React, if you're using the automatic JSX transform from React 17+, import React from "react" works safely — but in environments that don't, it's worth reviewing your moduleResolution and bundler settings together. In legacy CommonJS projects, there may be additional things to check due to the interplay with the moduleResolution change.
Pros and Cons
Advantages
| Item | Details |
|---|---|
Automatic baseUrl/paths rewriting |
No need to manually touch hundreds of files — handled with a single CLI command |
| Monorepo batch processing | Applied across all child tsconfigs by following project references and extends chains |
strict: true default |
New projects get strictNullChecks and noImplicitAny enabled without any extra configuration |
| ESM default alignment | Vite, esbuild, and Rollup-based projects are in optimal state without additional configuration |
Of these, the one I felt most in practice was the second one. In a project with around twelve tsconfigs in a monorepo, having a single tool handle all of them was practically the only saving grace of this migration.
Drawbacks and Caveats
| Item | Details | Mitigation |
|---|---|---|
--fixBaseUrl and --fixRootDir can't run together |
The two options can't be used simultaneously | Run each sequentially and validate the build after each |
Shared base tsconfig paths inheritance issues |
A structure where paths is defined in the base and baseUrl is set in a child isn't handled automatically |
Manually re-anchor path references |
| Legacy CJS project complexity | Keeping module: commonjs can cause conflicts with moduleResolution and esModuleInterop |
Explicitly pin module and moduleResolution |
Missing strict: false |
Projects that previously used loose type checking can see tens to hundreds of new errors | Explicitly add "strict": false or fix incrementally |
types: [] default |
Ambient types that were previously included globally are no longer auto-included | Explicitly specify "types": ["node"] etc. |
noUncheckedSideEffectImports enabled by default |
Affects side-effect imports like import './polyfill' |
Check type definitions for those imported files |
ignoreDeprecations is a stopgap |
This setting itself won't work in TypeScript 7.0 | Ultimately requires completing the real migration |
Most Common Mistakes in Practice
I personally experienced mistake #1, and it seems like many people make the same mistakes in the same order.
- Upgrading TypeScript to 6.0 before running
ts5to6— I did this first and got a flood of errors all at once, then had to roll back. It's much safer to run the tool first to clean up the tsconfig, then upgrade the version. - Not being aware of the
types: []change and mistaking disappeared Node.js global types for module errors — Ifprocess,Buffer, etc. suddenly throw errors, check for a missing"types": ["node"]first. It takes more time than you'd think to find the cause. - In a monorepo with a shared base tsconfig, only checking child tsconfigs and calling it done — The combination of
pathsdefined in the base tsconfig andbaseUrlin a child is not handled automatically byts5to6, so if you have a monorepo structure, review the base tsconfig separately as well.
Closing Thoughts
The tool handles 80%, and the remaining 20% is where mistakes happen — following the right order is everything with this migration.
Here are 3 steps you can start right now.
-
Start by auditing your tsconfig — Checking your current configuration from the project root will show you the scope of impact.
bash# Single project grep -E '"baseUrl"|"rootDir"|"strict"' tsconfig.json # Monorepo (recursive search) find . -name 'tsconfig*.json' | xargs grep -l '"baseUrl"\|"rootDir"\|"strict"' -
Run
ts5to6sequentially — The safe flow is: runnpx @andrewbranch/ts5to6 --fixBaseUrl ., validate the build, then runnpx @andrewbranch/ts5to6 --fixRootDir .and validate the build again. Checking the changes withgit diffafter each step makes problem tracking much easier. -
Fix
stricterrors incrementally — If fixing everything at once is too daunting, temporarily add"strict": false, then use a third-party tool like ts-strictify to apply it file by file. Note that adding a// @ts-strictcomment at the top of a file is a third-party tool convention, not an official TypeScript feature — confirm the tool is installed before relying on it.
References
- Announcing TypeScript 6.0 | TypeScript Official Blog
- TypeScript 6.0 Release Notes | Official Docs
- andrewbranch/ts5to6 | GitHub
- 6.0 Migration Guide — microsoft/TypeScript#62508 | GitHub
- TSConfig Reference | Official Docs
- @andrewbranch/ts5to6 | npm
- Announcing TypeScript 6.0 RC | TypeScript Official Blog