composite

Marks a project as referenceable, the prerequisite for project references and tsc --build.

Signature
"composite": boolean  // default: false
  • Added in TypeScript 3.0

Parameters

ParameterTypeDescription
$compositeoptionalboolean= falseEnsure TypeScript can determine where to find the outputs of this project so it can be referenced by others and built incrementally.

Return value

booleanA project whose outputs and build state are discoverable, so other projects may reference it and tsc --build can decide whether it is up to date.

Errors

Error TS6306 when a referenced project does not have composite enabled, and an error when an implementation file in the project is not matched by include or listed in files.

Changelog

  • PHP 3.0Introduced with project references and tsc --build.

composite is the flag that turns a tsconfig.json into a buildable unit. It exists so tsc --build can answer one question cheaply: are this project's outputs newer than its inputs? To make that answerable, it changes several defaults at once.

What it forces

  • declaration defaults to true — a referencing project consumes .d.ts, so there must be one.
  • incremental is on, and tsBuildInfoFile defaults into the project directory.
  • rootDir defaults to the directory containing the tsconfig.json rather than being inferred from the input files.
  • Every implementation file must be matched by include or listed in files; a stray file the compiler pulls in via an import but that no pattern matches is an error.

What trips people up

That last rule catches people constantly: a wide exclude, or a test file importing a source file outside include, fails a build that worked fine before. Also, composite is required on the project being referenced, not only on the one doing the referencing — TS6306, "Referenced project must have setting composite: true", means you put it in the wrong tsconfig.json. Pair it with declarationMap so editor navigation reaches source instead of generated declarations.

Example

Example · typescript
// packages/core/src/index.ts
// packages/core/tsconfig.json:
//   { "compilerOptions": { "composite": true, "declarationMap": true,
//                          "outDir": "dist", "rootDir": "src" },
//     "include": ["src/**/*"] }

export type UserId = string & { readonly __brand: "UserId" };

export function toUserId(raw: string): UserId {
  if (raw.length === 0) throw new Error("empty id");
  return raw as UserId;
}

// packages/api reads packages/core/dist/index.d.ts, not this source file.
const id = toUserId("u_42");
console.log(id, typeof id); // u_42 string

When to use it

  • Making a shared package in a monorepo consumable by other packages via project references.
  • Splitting src and test into separate buildable projects so tests cannot be imported by source.
  • Enabling tsc --build so dependencies rebuild in the right order automatically.
  • Getting per-package incremental builds instead of one whole-repo type-check.

Frequently asked questions

What does composite: true actually do?

It makes a project referenceable and buildable by tsc --build. Concretely it forces declaration output on, turns incremental on, defaults rootDir to the tsconfig's own directory, and requires every implementation file to be matched by include or files. Together those guarantees let the compiler locate a project's outputs and decide whether they are up to date without reading all its source.

How do I fix "Referenced project must have setting composite: true" (TS6306)?

Add "composite": true to the tsconfig.json of the project named in the references entry, not to the project doing the referencing. Every project that appears in someone's references array must be composite. Then build with tsc -b so the referenced project is compiled first.

Why do I get errors about files not being in the file list after enabling composite?

composite requires that all implementation files are matched by include or listed in files. If a source file gets pulled into the program through an import but no pattern matches it, the compiler reports it rather than silently compiling it. Widen include, narrow exclude, or move the file into the project it belongs to.

More examples

A referenceable package

declaration is already implied by composite but is written out for clarity. declarationMap is the addition that matters day to day: without it, Go to Definition from another package lands in dist/index.d.ts; with it, the editor opens src/index.ts. include covers every implementation file, which composite requires.

Example · json
// packages/core/tsconfig.json
{
  "compilerOptions": {
    "composite": true,
    "declaration": true,
    "declarationMap": true,
    "outDir": "dist",
    "rootDir": "src",
    "module": "nodenext",
    "strict": true
  },
  "include": ["src/**/*"]
}

The TS6306 mistake

Building packages/api fails with TS6306: Referenced project '../core' must have setting "composite": true. The flag belongs on the project being pointed at, because that is the one whose outputs tsc --build has to locate and date-check. Adding composite to core fixes it.

Example · json
// packages/api/tsconfig.json — the referencing project
{
  "compilerOptions": { "composite": true, "outDir": "dist", "rootDir": "src" },
  "references": [{ "path": "../core" }],
  "include": ["src/**/*"]
}

// packages/core/tsconfig.json — MISSING composite
{
  "compilerOptions": { "outDir": "dist", "rootDir": "src" },
  "include": ["src/**/*"]
}

Discussion

  • Be the first to comment on this lesson.