This TypeScript error occurs when you specify an output directory (--outDir) without also specifying a root directory (--rootDir). TypeScript requires both options to work together to properly structure compiled output. The fix involves adding a --rootDir option or removing --outDir if you don't need separate output directories.
The "Cannot use --outDir without --rootDir" error appears when you configure TypeScript to output compiled files to a different directory (using --outDir or the outDir option in tsconfig.json) but haven't specified a root directory (--rootDir or rootDir). TypeScript needs to know the root of your source files to properly preserve the directory structure when outputting to a different location. Without a root directory, TypeScript can't determine how to map source file paths to output file paths, which could lead to files being placed in incorrect locations or overwriting each other. This safety check prevents common issues where: 1. Files from different source directories end up in the same output location 2. Nested directory structures become flattened incorrectly 3. Source maps reference wrong file paths 4. Import paths in compiled code don't match the original structure The error ensures that when you use output directory redirection, TypeScript has enough information to maintain your project's organization in the compiled output.
The simplest fix is to add a rootDir property to your tsconfig.json that points to your source directory:
{
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
// other options...
},
"include": ["src/**/*"]
}The rootDir should be the common root directory of all your TypeScript source files. For most projects, this is ./src or . (current directory).
Verify your configuration:
# Check if TypeScript accepts the configuration
npx tsc --noEmit
# Or compile to see files placed correctly
npx tscAfter adding rootDir, TypeScript will preserve your directory structure in the output folder.
If you're compiling TypeScript directly from the command line without tsconfig.json, provide both flags:
# WRONG - missing --rootDir
npx tsc --outDir dist src/**/*.ts
# CORRECT - include both
npx tsc --outDir dist --rootDir src src/**/*.ts
# For multiple source directories
npx tsc --outDir dist --rootDir . src/**/*.ts tests/**/*.tsThe --rootDir flag tells TypeScript where your source files are relative to. This is especially important when you have files in multiple directories.
Common patterns:
# Single source directory
npx tsc --outDir dist --rootDir src src/**/*.ts
# Source files in current directory
npx tsc --outDir dist --rootDir . *.ts
# Multiple source patterns
npx tsc --outDir dist --rootDir . "src/**/*.ts" "lib/**/*.ts"If you don't actually need compiled files in a separate directory, simply remove the outDir option:
// Before - causes error
{
"compilerOptions": {
"outDir": "./dist"
// missing rootDir
}
}
// After - compiles in-place
{
"compilerOptions": {
// No outDir, files compile next to source
}
}This approach works well for:
- Small projects where compiled files can stay with source
- Development environments where you don't need clean separation
- Projects using bundlers (webpack, vite) that handle output
Compiled files will appear as .js files next to your .ts source files:
src/
├── index.ts → src/index.js
├── utils.ts → src/utils.js
└── types.ts → src/types.jsNote: This can clutter your source directory, so consider using .gitignore to exclude .js files.
For projects with nested or multiple source directories, set rootDir to the common ancestor:
project/
├── src/
│ ├── app/
│ │ └── index.ts
│ └── utils/
│ └── helpers.ts
├── tests/
│ └── app.test.ts
└── tsconfig.json{
"compilerOptions": {
"outDir": "./dist",
"rootDir": ".",
"include": ["src/**/*", "tests/**/*"]
}
}This produces:
dist/
├── src/
│ ├── app/
│ │ └── index.js
│ └── utils/
│ └── helpers.js
└── tests/
└── app.test.jsIf you want only src/ in dist/, exclude tests from compilation or use a different approach:
{
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
"include": ["src/**/*"]
}
}TypeScript might be reading multiple configuration files. Check for:
1. tsconfig.json in parent directories: TypeScript walks up the directory tree
2. extends configurations: Your tsconfig.json might extend another config
3. Project references: Using "references" in tsconfig.json
# See which config file TypeScript is using
npx tsc --showConfig
# Check for extended configs
cat tsconfig.json | grep "extends"
# Find all tsconfig.json files
find . -name "tsconfig.json" -type fIf using extends:
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
}
}Make sure the base config doesn't have conflicting outDir/rootDir settings.
Use TypeScript's diagnostic tools to understand the issue:
# Show all compiler options being used
npx tsc --showConfig
# Dry run to see what would be compiled
npx tsc --listFiles
# Check for configuration errors
npx tsc --diagnostics
# Trace module resolution (can help with path issues)
npx tsc --traceResolutionCommon issues to check:
- Multiple tsconfig.json files: Use --project flag to specify which
- Conflicting command-line flags: Flags override tsconfig.json
- Incorrect file patterns: Ensure include/exclude patterns match your files
- Path mapping conflicts: paths option may need rootDir adjustment
After fixing, verify the output structure:
# Compile and check output
npx tsc
tree dist/ # or ls -R dist/### Understanding rootDir and outDir Relationship
The rootDir and outDir options work together to transform source paths to output paths:
// Source: /project/src/app/index.ts
// rootDir: /project/src
// outDir: /project/dist
// Output: /project/dist/app/index.jsTypeScript calculates: outputPath = outDir + (sourcePath - rootDir)
Important constraints:
1. All source files must be under rootDir (or TypeScript will error)
2. rootDir must not be outside the project directory
3. Symlinks and node_modules are handled specially
### Composite Projects and Project References
When using TypeScript's project references feature, each referenced project needs its own rootDir/outDir configuration:
// tsconfig.json (root)
{
"references": [
{ "path": "./packages/core" },
{ "path": "./packages/ui" }
],
"compilerOptions": {
"composite": true,
"outDir": "./dist",
"rootDir": "."
}
}
// packages/core/tsconfig.json
{
"compilerOptions": {
"outDir": "../../dist/packages/core",
"rootDir": "."
}
}Each project's rootDir is relative to its own tsconfig.json location.
### Monorepo Considerations
In monorepos with tools like npm/yarn/pnpm workspaces:
1. Each package needs its own tsconfig.json with appropriate rootDir
2. Root tsconfig.json may use "references" to link packages
3. Path aliases may need adjustment based on rootDir
4. Build tools (like tsc --build) handle the composite build
### Alternative: Using baseUrl and paths
If you're using path mapping, rootDir becomes more critical:
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"@components/*": ["components/*"],
"@utils/*": ["utils/*"]
},
"outDir": "./dist",
"rootDir": "./src"
}
}With this setup:
- baseUrl: Where non-relative module names are resolved from
- rootDir: The root of all source files
- outDir: Where compiled files go
All three must be consistent for proper compilation.
### Build Tools Integration
Different build tools handle outDir/rootDir differently:
Webpack with ts-loader: Usually ignores outDir/rootDir, outputs based on webpack config
Vite: Uses esbuild, respects tsconfig but may have different behavior
ts-node: Runs TypeScript directly, doesn't use outDir
tsc --watch: Respects outDir/rootDir and outputs to specified directory
Always check your build tool's documentation for TypeScript integration specifics.
### Debugging Output Path Issues
If files aren't appearing where expected:
# Show what would be emitted without actually writing
npx tsc --listEmittedFiles
# See verbose compilation details
npx tsc --verbose
# Check file-by-file what's happening
npx tcc --extendedDiagnosticsCommon output path problems:
1. Files missing from output: Check include/exclude patterns
2. Wrong directory depth: rootDir might be set incorrectly
3. Extra nested directories: Source files outside expected hierarchy
4. Empty output: No files match include patterns or all excluded
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