This error occurs when using TypeScript project references but the referenced projects don't have 'composite: true' in their tsconfig.json. Project references require each referenced project to be marked as composite to enable fast incremental builds and proper type resolution.
The "'composite' option is required for project references" error indicates that your TypeScript configuration is trying to use project references (the `references` field in tsconfig.json), but one or more of the referenced projects doesn't have `"composite": true` in their tsconfig.json file. TypeScript project references are a feature that allows you to structure a monorepo or multi-package repository where TypeScript projects can depend on other TypeScript projects. When you use project references, TypeScript needs to know where the compiled output (.d.ts files) of each referenced project is located. The `composite` option serves several purposes: 1. **Marks a project as compilable**: Tells TypeScript this is a self-contained project that can be compiled independently 2. **Enforces constraints**: Ensures the project has proper rootDir and outDir settings for reliable builds 3. **Enables declaration maps**: Generates source maps for declaration files for better IDE support 4. **Supports incremental builds**: Allows TypeScript's build mode (tsc --build) to determine which projects are out of date Without the `composite` flag, TypeScript cannot establish proper boundaries between projects and cannot reliably resolve cross-project types. This is why the flag is required when using project references.
The most straightforward fix is to add "composite": true to each referenced project's compiler options.
Step 1: Locate the referenced project's tsconfig.json
If your project structure looks like:
monorepo/
├── packages/
│ ├── shared/
│ │ └── tsconfig.json <- Referenced project
│ └── app/
│ └── tsconfig.json <- Main project
└── tsconfig.json <- Root configStep 2: Edit the referenced project's tsconfig.json
Open packages/shared/tsconfig.json and add composite:
{
"compilerOptions": {
"composite": true,
"declaration": true,
"declarationMap": true,
"outDir": "./dist",
"rootDir": "./src",
"moduleResolution": "node",
"target": "ES2020",
"module": "commonjs"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}Important related settings:
- declaration: true - Required with composite to generate .d.ts files
- outDir - Must be explicitly set when composite is true
- rootDir - Should be explicitly set for clarity
Step 3: Do this for ALL referenced projects
Every project listed in the references array must have composite: true.
Check that your main project's tsconfig.json properly references the composite projects.
Main project tsconfig.json example:
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
"target": "ES2020",
"module": "commonjs",
"declaration": true
},
"include": ["src/**/*"],
"references": [
{ "path": "../shared" },
{ "path": "../utils" }
]
}Key points:
- The path in references points to the directory containing the referenced project's tsconfig.json
- Each referenced project must have its own tsconfig.json file
- The path can be relative or absolute
- All referenced projects must have composite: true (from Step 1)
Verify the structure:
# Check each referenced project has tsconfig.json
ls packages/shared/tsconfig.json
ls packages/utils/tsconfig.json
# Check that composite is set
grep -A 2 '"compilerOptions"' packages/shared/tsconfig.jsonUse TypeScript's build mode to verify the project references work correctly.
Run build mode compilation:
# Compile all projects in dependency order
npx tsc --build
# Clean build (remove all outputs first)
npx tsc --build --clean
# Verbose output to see what's happening
npx tsc --build --verbose
# Force recompile all projects
npx tsc --build --forceExpected output on success:
Successfully compiled 3 project(s) and their dependenciesCheck the output structure:
# Verify .d.ts files were generated
find packages -name "*.d.ts" | head -10
# Check that dist directories were created
ls -la packages/shared/dist/
ls -la packages/utils/dist/If it still fails:
- Review the error message carefully
- Check if all referenced projects have composite: true
- Verify declaration: true is set in referenced projects
- Ensure outDir is explicitly defined in all tsconfig.json files
After fixing composite settings, configure your IDE for proper type checking.
For VS Code:
1. Open the TypeScript version selector:
- Open Command Palette (Ctrl+Shift+P / Cmd+Shift+P)
- Type "TypeScript: Select TypeScript Version"
- Choose "Use Workspace Version"
2. Enable project references in settings:
.vscode/settings.json{
"typescript.tsserver.experimental.enableProjectDiagnostics": true,
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}3. Reload the TypeScript language server:
- Command Palette > "Developer: Reload Window"
For other IDEs:
- WebStorm: File > Settings > Languages & Frameworks > TypeScript > Enable project references
- Vim/Neovim with coc-tsserver: No special configuration needed
- Sublime with LSP: The LSP client should automatically detect project references
Verify IDE recognizes project references:
- Hover over imports from referenced projects - types should resolve
- Jump to definition (Ctrl+Click) should navigate to source files
- No red squiggles on cross-project imports
Ensure your build process uses tsc --build to respect project references.
Update package.json scripts:
{
"scripts": {
"build": "tsc --build",
"build:clean": "tsc --build --clean && tsc --build",
"build:force": "tsc --build --force",
"typecheck": "tsc --noEmit --build",
"dev": "tsc --build --watch"
}
}For monorepo tools:
If using workspace tools (npm workspaces, yarn workspaces, pnpm), they may have their own build orchestration:
With npm workspaces:
npm run build # Builds all workspaces in dependency orderFor build tools like Nx:
Nx automatically understands TypeScript project references:
nx build # Respects references and cachesIn CI/CD (GitHub Actions example):
- name: Build TypeScript projects
run: npx tsc --build
- name: Type check
run: npx tsc --noEmit --buildImportant: Always use tsc --build instead of tsc when you have project references. The --build flag ensures proper dependency resolution and incremental compilation.
Understanding TypeScript Project References Architecture
Project references introduce a concept of project boundaries in TypeScript. Each composite project is treated as a self-contained unit with:
- Its own tsconfig.json
- Explicit input files (via include/files)
- Explicit output directory (outDir)
- Generated declaration files (.d.ts)
When project A references project B, TypeScript resolves types from project B's generated .d.ts files, not the source .ts files. This creates a clear contract between projects.
Why declare and declarationMap are required:
When composite: true is set, TypeScript enforces these settings:
- declaration: true - Must generate .d.ts files so other projects can use types
- declarationMap: true - Recommended for IDE support; maps declarations back to source
Without these, the .d.ts files won't be generated, and project references can't work.
Incremental builds with --build:
The magic of project references is the tsc --build mode:
1. Dependency analysis: TypeScript reads all tsconfig.json files and builds a dependency graph
2. Out-of-date detection: Uses timestamps of source files vs output files
3. Smart compilation: Only recompiles projects that are actually out of date
4. Correct order: Compiles projects in dependency order
Example:
shared (no deps) -> compiles first
utils (depends on shared) -> compiles next
app (depends on utils) -> compiles lastIf you only modify a file in shared, running tsc --build will:
- Recompile shared
- Recompile utils (because its dependency changed)
- Recompile app (because utils changed)
But if you only modify app and run tsc --build, only app recompiles.
Common mistakes to avoid:
1. Not setting declaration: true
- Without it, .d.ts files won't be generated
- Other projects can't access types
2. Not setting explicit outDir
- TypeScript can't reliably find the output
- May cause "Cannot find module" errors
3. Using tsc instead of tsc --build
- Works but skips dependency ordering
- Slower and less reliable with multiple projects
4. Mixing composite and non-composite projects
- All referenced projects must be composite
- Non-composite projects can still exist but can't be referenced
5. Circular references
- Project A references B, B references A
- TypeScript will error; restructure to avoid cycles
Monorepo vs multi-package considerations:
Project references work well for:
- Monorepos: Multiple packages in one repo with shared dependencies
- Large projects: Splitting one large project into logical pieces
- Shared libraries: A core library with multiple dependent packages
Limitations:
- All projects must be in the same repository
- Cannot reference projects from different repos or packages
- Each project needs its own build output directory
Workspace vs project references:
These are different concepts:
- npm/yarn/pnpm workspaces: Package manager feature for dependency management
- TypeScript project references: TypeScript compiler feature for build coordination
You typically use both together in a monorepo setup.
Migration from non-composite to composite:
If adding project references to an existing project:
1. Add composite: true to all projects
2. Ensure declaration: true is set
3. Set explicit outDir in all projects
4. Run tsc --build --force to rebuild everything
5. Test that imports still work
6. Update build scripts to use --build
7. Update CI/CD pipelines
Performance benefits:
With proper project references:
- Initial build: ~5-10% slower (need to generate .d.ts)
- Incremental builds: 50-80% faster (only changed projects)
- Watch mode: Significant speed improvement
- IDE responsiveness: Better with declaration maps
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