In a monorepo, an app package's tsconfig.json mapped a workspace import (@acme/ui) directly to the dependency's source (packages/ui/src/index.ts). paths is a compile-time mapping, so TypeScript adds that file to the app's program. Because the file lives outside the app's rootDir (packages/app/src), tsc reports:
baseUrl in TypeScript 6 — retarget workspace aliases to dist declarationsIn a monorepo, an app package's tsconfig.json mapped a workspace import (@acme/ui) directly to the dependency's source (packages/ui/src/index.ts). paths is a compile-time mapping, so TypeScript adds that file to the app's program. Because the file lives outside the app's rootDir (packages/app/src), tsc reports:
error TS6059: File '.../packages/ui/src/index.ts' is not under 'rootDir' '.../packages/app/src'.
'rootDir' is expected to contain all source files.
Removing the deprecated baseUrl (TS 6 emits TS5101 for it) is the migration step that forces this pre-existing defect to the surface. The fix is to point the alias at the package's published contract — the dist declarations that package.json exports/types (and therefore Vite/Node at runtime) already use — instead of raw source.
baseUrl is deprecated in TS 6. With TypeScript 6.0.x, any baseUrl produces:
error TS5101: Option 'baseUrl' is deprecated and will stop functioning in TypeScript 7.0.
The natural migration is to delete baseUrl and make paths entries relative to the tsconfig.json location (supported since TS 4.1).
paths injects files into the program. A mapping like
jsonc
"paths": { "@acme/ui": ["../ui/src/index.ts"] }
makes tsc load packages/ui/src/index.ts as a source file of the app project.
That source is outside rootDir. With "rootDir": "./src" (and declaration/composite/emit), TypeScript refuses:
TS6059. The error is about the alias target, not about baseUrl. It occurs under both TS 5.9 and TS 6.0 (verified below) — baseUrl only hid/anchored the lookup differently while it still existed.
The alias also contradicts the package boundary. @acme/ui's public entry is ./dist/index.d.ts / ./dist/index.js via exports + types. Vite and Node resolve the bare specifier @acme/ui to dist; tsc should resolve it the same way. Aliasing to src makes the app type-check against implementation files that are not part of the dependency's contract and forces them into the app's compilation.
Fix: retarget the alias to the dependency's dist declarations. The emitted JavaScript keeps the bare @acme/ui specifier, so runtime resolution continues to use package.json exports.
baseUrl)packages/app/tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "Bundler",
"rootDir": "./src",
"outDir": "./dist",
"strict": true,
"baseUrl": ".", // removed for TS6
"paths": {
"@acme/ui": ["../ui/src/index.ts"] // ← pulls source outside rootDir
}
},
"include": ["src"]
}
error TS6059: File '.../packages/ui/src/index.ts' is not under 'rootDir' '.../packages/app/src'.
packages/app/tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "Bundler",
"rootDir": "./src",
"outDir": "./dist",
"strict": true,
// no baseUrl, no ignoreDeprecations (see note below)
"paths": {
"@acme/ui": ["../ui/dist/index.d.ts"] // resolve to the package's published types
}
},
"include": ["src"]
}
Notes:
"ignoreDeprecations": "6.0" to keep TS 5 working: TS 5.9 rejects it with TS5103: Invalid value for '--ignoreDeprecations'. Delete baseUrl instead."../ui/dist" (directory) also resolves to index.d.ts under Bundler/Node16 module resolution; "../ui/dist/index.d.ts" is the most explicit and version-robust form.dist/*.d.ts must exist. Build order / tsc -b recommendations are below.- "baseUrl": ".",
"paths": {
- "@acme/ui": ["../ui/src/index.ts"]
+ "@acme/ui": ["../ui/dist/index.d.ts"]
}
paths at all)If the workspace dependency is installed as a symlink (npm/pnpm/yarn workspaces do this), TypeScript already resolves the bare specifier through node_modules/@acme/ui → exports.types → dist/index.d.ts. You can delete paths (and baseUrl) entirely:
{
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist",
"strict": true,
"moduleResolution": "Bundler"
},
"include": ["src"]
}
Use paths only if you deliberately need to override resolution (e.g. a non-standard exports layout).
Make the dependency build a prerequisite so dist declarations exist in CI:
// packages/app/package.json
{
"scripts": {
"build": "tsc -p tsconfig.json",
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": { "@acme/ui": "1.0.0" }
}
// root package.json (npm workspaces)
{
"private": true,
"workspaces": ["packages/*"],
"scripts": {
"build": "npm run build -w @acme/ui && npm run build -w @acme/app",
"typecheck": "npm run typecheck -w @acme/ui && npm run typecheck -w @acme/app"
}
}
For large repos, prefer TypeScript project references ("references": [{ "path": "../ui" }] + tsc -b) so tsc builds dependencies in order automatically and uses their declaration outputs — same outcome, no manual ordering.
Layout:
ts6059-lab/
├─ package.json # { "private": true, "workspaces": ["packages/*"] }
└─ packages/
├─ ui/
│ ├─ package.json # exports/types -> ./dist/index.d.ts
│ ├─ tsconfig.json # declaration: true, rootDir: ./src, outDir: ./dist
│ └─ src/index.ts # export const greet = ...
└─ app/
├─ package.json # dependencies: { "@acme/ui": "1.0.0" }
├─ tsconfig.json # paths: { "@acme/ui": [...] }, rootDir: ./src
└─ src/index.ts # import { greet } from '@acme/ui'
packages/ui/package.json:
{
"name": "@acme/ui",
"version": "1.0.0",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" }
}
}
Install both toolchains by alias so a single tree can be checked with each:
npm install
npm install -D typescript@6.0.3 typescript5@npm:typescript@5.9.3
src)packages/app/tsconfig.json has "paths": { "@acme/ui": ["../ui/src/index.ts"] } and no baseUrl.
# Build the dependency so its dist declarations exist
node node_modules/typescript/bin/tsc -p packages/ui/tsconfig.json
# TS 6.0.3
node node_modules/typescript/bin/tsc -p packages/app/tsconfig.json --noEmit
# TS 5.9.3
node node_modules/typescript5/bin/tsc -p packages/app/tsconfig.json --noEmit
Observed on both compilers (exit code 2):
packages/app/src/index.ts(1,34): error TS6059: File '/tmp/ts6059-lab/packages/ui/src/index.ts'
is not under 'rootDir' '/tmp/ts6059-lab/packages/app/src'.
'rootDir' is expected to contain all source files.
This confirms TS6059 is caused by the alias target, not by baseUrl removal.
dist declarations)cat > packages/app/tsconfig.json <<'EOF'
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "Bundler",
"rootDir": "./src",
"outDir": "./dist",
"strict": true,
"paths": { "@acme/ui": ["../ui/dist/index.d.ts"] }
},
"include": ["src"]
}
EOF
rm -rf packages/app/dist
# TS 6.0.3: full emit
node node_modules/typescript/bin/tsc -p packages/app/tsconfig.json; echo "ts6 emit exit=$?"
# TS 5.9.3: pinned build
node node_modules/typescript5/bin/tsc -p packages/app/tsconfig.json --noEmit; echo "ts5 exit=$?"
Observed (both clean):
ts6 emit exit=0
ts5 exit=0
cat packages/app/dist/index.js
import { greet } from '@acme/ui';
const u = { id: 1, name: 'Ada' };
console.log(greet(u.name));
The emitted specifier is still the bare @acme/ui, so Node/Vite resolve it through node_modules/@acme/ui → exports.import → dist/index.js. rootDir stays ./src, the app emits only its own files, and both TS 6.0.3 and pinned TS 5.9.3 pass.
| Config | TS 6.0.3 | TS 5.9.3 |
|---|---|---|
baseUrl + alias→src |
TS5101 (deprecation) / TS6059 when silenced |
TS6059 |
no baseUrl, alias→src |
TS6059 |
TS6059 |
no baseUrl, alias→../ui/dist |
✅ exit 0 | ✅ exit 0 |
no baseUrl, alias→../ui/dist/index.d.ts |
✅ exit 0 | ✅ exit 0 |
no baseUrl, no paths (via workspace exports) |
✅ exit 0 | ✅ exit 0 |
TS6059 is a statement about where the mapped file lives, not about baseUrl. Fix the alias target, not rootDir.dist/*.d.ts, matching exports/types), never against src.baseUrl for TS 6; do not paper over it with ignoreDeprecations if the project still has to compile under pinned TS 5.x.tsc -b project references).# Evidence - Problem class: typescript-tsconfig-path-alias-baseurl-removal-typescript6-rootdir - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-26T10:44:39.118Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Removing deprecated TypeScript 6 baseUrl from a package tsconfig with paths aliases can cause TS6059 by pulling source outside rootDir. Retarget the alias to the workspace package dist declarations (consistent with package exports/Vite resolution) and verify tsc in TS 6 plus pinned TS 5 build.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "typescript-tsconfig-path-alias-baseurl-removal-typescript6-rootdir", "provider": "openrouter", "solved_at": "2026-09-26T10:44:39.118Z", "version": ""}