← All insights
ENGINEERING JOURNAL / Software Engineering

TypeScript project references: why clean CI builds fail

Trace missing declarations and stale outputs in TypeScript project references, then establish a reproducible dependency build without relying on editor caches.

CLOUVENIX / TYPESCRIPT ENGINEERINGBuild declarations in dependency order01 / APPPACKAGE A02 / QUEUEDECLARATIONS03 / BRANCHPACKAGE B04 / GUARDCLEAN BUILDConceptual flow / source contracts, generated declarations, clean acceptance
Build declarations in dependency order — conceptual flow, not a deployment topology

A working editor is not a clean build

Project references divide a TypeScript codebase into cooperating compiler projects. A shared domain package might be used by an API and an administrative application. The dependency is a build relationship as well as an import relationship. If a developer has old generated declarations on disk, an editor may look healthy while a clean CI worker cannot compile the same repository.

Keep three questions separate: can TypeScript understand an import, can the build produce every dependency, and can the runtime resolve the emitted package? Passing one does not automatically answer the others. This guide focuses on declaration production and dependency order rather than runtime input validation or application state modeling.

Setup: describe the actual dependency graph

Inventory each tsconfig, package entry point, source directory and output directory. Record which packages depend on which others. For referenced projects, review the composite and declaration requirements in the documentation for the pinned compiler version. Make sure generated outputs do not overlap source files or outputs owned by another project.

From a disposable clean checkout, install from the lockfile and run the documented project build command. TypeScript's build mode, invoked with tsc -b, discovers referenced projects and builds those requiring work in dependency order. Ordinary tsc invocation is not an automatic replacement for this orchestration. Use the locally installed compiler so an unrelated global installation cannot change the result.

Illustrative incident: only one workstation passes

An illustrative application imports an internal pricing package. A developer's tests pass because yesterday's declaration files remain present. CI starts clean and reports that an expected dependency output has not been built. Re-running the editor does not establish whether the package graph is correct. No customer engagement or performance improvement is claimed by this example.

Capture the compiler version, command, first failing diagnostic, referenced config paths and package output listing. Preserve the failing clean build before trying multiple fixes. An error later in the log may simply be a consequence of the first missing dependency.

Troubleshooting: identify the missing build edge

SymptomInspectQuestion to answer
Dependency declaration is absentReference list and build entry pointWas the dependency included in the graph?
Local output differs from CIIgnored files and clean checkout resultIs a stale artifact hiding the defect?
Compiler succeeds, runtime import failsPackage exports and emitted pathsDoes runtime resolution match the built package?
Editing one package changes another outputoutDir and rootDir ownershipAre output directories overlapping?

Do not fix a missing reference by copying declaration files manually into CI. That creates an artifact with unclear provenance. Similarly, suppressing the diagnostic may leave consumers compiling against an obsolete contract while the runtime uses different JavaScript.

Corrective action and artifact policy

Correct the reference graph and give every project a clear output boundary. Decide whether generated outputs are built in CI, published as versioned packages or deliberately checked in. Apply that policy consistently. Build-mode dry and verbose options can help review the intended work before changing the pipeline, but retain a real clean build as acceptance evidence.

Keep incremental metadata and build caches scoped to the appropriate inputs and toolchain. A cache is an optimization, not the source of truth. When diagnosing, reproduce once without restored outputs in a disposable workspace; do not delete a developer's uncommitted source or production application directory. If dependency cycles appear, review package responsibilities rather than forcing an arbitrary order that only happens to pass today.

Verification, SLO and SOP

Run the clean build, change a shared exported type, and confirm that consumers are rebuilt and checked. Then test the emitted package with the actual runtime or bundler used by the application. Include a negative fixture whose consumer violates the changed contract; a build that incorrectly passes it is not protecting the boundary.

An internal build-reliability SLO can measure reproducible successful builds for valid changes, with failures attributed to code, infrastructure or dependency availability. It is distinct from a customer-facing application SLA. The SOP should record the build entry point, compiler version source, artifact ownership and safe cache-bypass procedure. Avoid promising a build speed gain without measurements from representative changes.

See JavaScript and TypeScript development and DevOps automation for coordinated application and pipeline work.

Technical reference

TypeScript project references, reviewed 19 October 2026. Compiler behavior should be checked against the pinned project version.