This TypeScript error occurs when a source file is located outside the directory specified by the rootDir compiler option. TypeScript requires all source files to be within rootDir to maintain a predictable output structure. The fix involves reorganizing files, adjusting tsconfig.json settings, or removing the rootDir constraint.
The "file is not under 'rootDir'" error (TS6059) appears when TypeScript encounters a source file that exists outside the directory specified by the rootDir compiler option in tsconfig.json. TypeScript uses rootDir to determine the root of the input file structure, which in turn defines the structure of the output directory. When you set rootDir, TypeScript enforces that all source files must be within that directory or its subdirectories. This ensures that when files are compiled to the outDir, the relative directory structure is preserved and predictable. If a file is outside rootDir, TypeScript cannot determine where to place the compiled output, leading to this error. This error commonly occurs in several scenarios: 1. **Project restructuring**: Files were moved or the project structure changed without updating tsconfig.json 2. **Incorrect configuration**: The rootDir is set too narrowly (e.g., to "src") but some files exist outside that directory 3. **Build scripts**: Configuration files or scripts in the project root try to import from src 4. **Monorepos**: Cross-project imports that violate individual project boundaries 5. **Test files**: Test files located outside the main source directory are accidentally included
Run the TypeScript compiler to see the full error message:
npx tsc --listFiles | grep -v node_modulesThe error will tell you exactly which file is problematic:
error TS6059: File '/project/setupTests.ts' is not under 'rootDir' '/project/src'.
'rootDir' is expected to contain all source files.Make note of:
- The file path that's causing the issue
- The current rootDir value
- Whether this file should be compiled or excluded
If the files should be compiled, move them into your rootDir:
# Example: Moving a config file into src
mv setupTests.ts src/setupTests.ts
# Update any imports that reference this file
# Before: import './setupTests'
# After: import './src/setupTests'For test configuration files:
# Create a test setup directory inside src
mkdir -p src/test
mv setupTests.ts src/test/
mv jest.config.ts src/test/After moving files, update imports throughout your project and run:
npx tscIf the files outside rootDir don't need to be compiled (like config files), exclude them from compilation:
{
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist"
},
"include": ["src/**/*"],
"exclude": [
"node_modules",
"**/*.spec.ts",
"**/*.test.ts",
"cypress",
"setupTests.ts",
"jest.config.ts"
]
}Common files to exclude:
- Test files: **/*.test.ts, **/*.spec.ts
- Config files: *.config.ts, setupTests.ts
- Test directories: cypress/, e2e/, __tests__/
- Build scripts: scripts/, tools/
If you don't need an explicit rootDir, remove it and let TypeScript infer it automatically:
{
"compilerOptions": {
// Remove this line:
// "rootDir": "./src",
"outDir": "./dist",
"moduleResolution": "node"
},
"include": ["src/**/*"]
}TypeScript will automatically determine the common root directory of all included files. This is often the simplest solution.
Alternatively, expand rootDir to include all source files:
{
"compilerOptions": {
"rootDir": ".", // Project root instead of ./src
"outDir": "./dist"
},
"include": ["src/**/*", "setupTests.ts"]
}However, this will change your output structure - files from src/ will be in dist/src/ instead of dist/.
For projects with multiple compilation contexts (app code + tests + scripts), create separate tsconfig files:
tsconfig.json (base configuration):
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true,
"esModuleInterop": true
}
}tsconfig.build.json (production build):
{
"extends": "./tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist"
},
"include": ["src/**/*"],
"exclude": ["src/**/*.test.ts", "src/**/*.spec.ts"]
}tsconfig.test.json (for tests):
{
"extends": "./tsconfig.json",
"compilerOptions": {
"rootDir": ".",
"outDir": "./dist-test"
},
"include": ["src/**/*", "**/*.test.ts", "setupTests.ts"]
}Then use the appropriate config:
# Build production code
npx tsc -p tsconfig.build.json
# Type-check tests
npx tsc -p tsconfig.test.json --noEmitIf you're in a monorepo with multiple packages, use project references:
packages/app/tsconfig.json:
{
"compilerOptions": {
"composite": true,
"rootDir": "./src",
"outDir": "./dist",
"declarationDir": "./dist/types"
},
"include": ["src/**/*"]
}packages/shared/tsconfig.json:
{
"compilerOptions": {
"composite": true,
"rootDir": "./src",
"outDir": "./dist"
},
"include": ["src/**/*"]
}Root tsconfig.json:
{
"files": [],
"references": [
{ "path": "./packages/app" },
{ "path": "./packages/shared" }
]
}Build all projects:
npx tsc --buildThis keeps each package's rootDir separate and properly isolated.
### How rootDir Affects Output Structure
The rootDir option controls the structure of your compiled output. Consider this project:
project/
├── src/
│ ├── index.ts
│ └── utils/
│ └── helper.ts
└── tsconfig.jsonWith rootDir: "./src":
{
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist"
}
}Output:
dist/
├── index.js
└── utils/
└── helper.jsWithout rootDir (or with rootDir: "."):
{
"compilerOptions": {
"outDir": "./dist"
}
}Output:
dist/
└── src/
├── index.js
└── utils/
└── helper.jsThe rootDir is stripped from the output path, so setting it correctly is important for deployment.
### Why TypeScript Enforces This
TypeScript's strict rootDir enforcement exists for good reason:
1. Predictable output: Every input file has an unambiguous output location
2. Prevent accidental compilation: Configuration files or scripts outside your source tree aren't accidentally compiled
3. Build reproducibility: The same input structure always produces the same output structure
If TypeScript allowed files outside rootDir, it would face two bad options:
- Write output files outside outDir (breaks isolation)
- Flatten the directory structure (loses organization)
### Working with Path Mapping
Path mapping in tsconfig.json doesn't bypass rootDir requirements:
{
"compilerOptions": {
"rootDir": "./src",
"baseUrl": ".",
"paths": {
"@config/*": ["config/*"] // config is outside src!
}
}
}If config/ is outside src/, you'll still get the rootDir error even with path mapping. Solutions:
1. Move config into src: src/config/
2. Import config as a package, not source files
3. Use separate tsconfig for config with its own rootDir
### Declaration Files and rootDir
When using declaration: true, the same rootDir constraints apply to .d.ts output:
{
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist",
"declaration": true,
"declarationDir": "./types" // Must match rootDir structure
}
}Both JavaScript and declaration files respect the rootDir structure.
### Composite Projects and rootDir
In composite projects (used for project references), rootDir behaves differently:
{
"compilerOptions": {
"composite": true,
// rootDir is inferred from the "include" pattern
"outDir": "./dist"
},
"include": ["src/**/*"]
}With composite: true, TypeScript is more strict about file organization because it builds incremental .tsbuildinfo files.
### Debugging rootDir Issues
See what TypeScript considers the rootDir:
# Show all files TypeScript is processing
npx tsc --listFiles
# Show detailed compiler options
npx tsc --showConfigThe --showConfig output will show the resolved rootDir value, which might be different from what you specified if TypeScript inferred it.
### Avoiding the Problem Entirely
Best practices to prevent this error:
1. Single source directory: Keep all source code in src/, all config in root
2. Explicit include: Use "include": ["src/**/*"] to be explicit
3. Let TypeScript infer: Don't set rootDir unless you need specific output structure
4. Separate configs: Use tsconfig.build.json for build, base tsconfig.json for IDE
5. Test isolation: Keep tests in src/__tests__/ or use separate tsconfig.test.json
Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript