"Project references partition a monorepo so the compiler can avoid re-checking what hasn't changed."
The problem
In a monorepo with multiple TypeScript packages (e.g., packages/api, packages/web, packages/shared), a naive tsc rechecks everything every time. For large monorepos this can take minutes — way too long for a development loop.
Project references
Each package gets its own tsconfig.json. The root tsconfig.json lists all the packages as references. The packages also reference each other via the same field where they depend.
tsc -b (build mode) reads the references, computes the dependency graph, and compiles in topological order. The result is cached in .tsbuildinfo files. On subsequent runs, only packages whose inputs changed are recompiled — and only their direct dependents need to be re-checked.
Each package's tsconfig needs composite: true
Project-reference packages must set "composite": true in their tsconfig. This enables incremental builds, requires explicit outDir, and forces declaration emission (so dependents can read the types). The flag is the opt-in.