TypeScript Project References: composite, tsc -b and Shared Types for CLI and API
November 6, 2026
Welcome to episode 49 of TypeScript from Zero, a series for complete beginners (no JavaScript needed). Welcome back to TypeScript from zero! Our task tracker has a command line tool and an HTTP server. Both need the same types. Today we split the code into three packages, a shared one, a cli and an api, and we teach tsc how they depend on each other. The tool for that is project references. We are in a new folder called mono. Every code block below is the exact code and output from the video, ready to copy.
- All episodes are in the TypeScript from Zero playlist.
- Versions used: Node.js 26.13.0, npm 11.20.0, TypeScript 7.0.2.
Setup
We start in the terminal and open the project in VS Code:
cd mono
code .1. The folder
The folder is prepared. It is an npm workspace: three packages under packages, and npm links them, so that one package can import another by its name, at tasks slash shared. Every package has its own T S config file, and they all extend this base file. Look at the option composite. It marks a project as one that other projects may depend on. It needs declaration files, which we also switch on.
2. Shared
The shared package holds the task type, and a sample value. The cli package imports it by name, and prints the title.
3. Problem
Now we build the cli, with tsc dash b, for build mode. And it fails: cannot find module tasks slash shared. TS2307. The shared package has not been built yet, so there are no declaration files, and tsc has nothing to read.
packages/cli/src/main.ts:
import { sample } from "@tasks/shared";
console.log("cli:", sample.title);packages/shared/src/task.ts:
export type Task = { id: number; title: string };
export const sample: Task = { id: 1, title: "Buy milk" };$ npx tsc -b packages/cli
packages/cli/src/main.ts(1,24): error TS2307: Cannot find module '@tasks/shared' or its corresponding type declarations.4. references
The cli must say that it depends on shared. In its T S config file we add references, with the path to the shared package. Now build mode knows the order: shared first, then the cli. It builds both with one command. In the shared folder, a dist folder appears, with the declaration file that the cli needed.
packages/cli/tsconfig.json:
{
"extends": "../../tsconfig.base.json",
"compilerOptions": { "rootDir": "./src", "outDir": "./dist" },
"include": ["src"],
"references": [{ "path": "../shared" }]
}$ npx tsc -b packages/cli$ ls packages/shared/dist
task.d.ts
task.js5. The api
The api package gets the same treatment: a source file, and the reference. Then one command builds everything that is needed. And both programs run, from their dist folders, with the same shared type.
packages/api/src/server.ts:
import { sample } from "@tasks/shared";
console.log("api:", sample.id);packages/api/tsconfig.json:
{
"extends": "../../tsconfig.base.json",
"compilerOptions": { "rootDir": "./src", "outDir": "./dist" },
"include": ["src"],
"references": [{ "path": "../shared" }]
}$ npx tsc -b packages/cli packages/api$ node packages/cli/dist/main.js
cli: Buy milk$ node packages/api/dist/server.js
api: 16. Incremental
Build mode is incremental. We run it again with -- verbose, and it tells us that every project is up to date, because no input is newer than its output. Nothing is built twice. In a big code base this saves minutes. And when the shared types change, only the packages that depend on them are rebuilt.
$ npx tsc -b --verbose packages/cli packages/api
04:15:43 PM - Projects in this build:
* packages/shared/tsconfig.json
* packages/cli/tsconfig.json
* packages/api/tsconfig.json
04:15:43 PM - Project 'packages/shared/tsconfig.json' is up to date because newest input 'packages/shared/src/task.ts' is older than output 'packages/shared/tsconfig.tsbuildinfo'
04:15:43 PM - Project 'packages/cli/tsconfig.json' is up to date because newest input 'packages/cli/src/main.ts' is older than output 'packages/cli/tsconfig.tsbuildinfo'
04:15:43 PM - Project 'packages/api/tsconfig.json' is up to date because newest input 'packages/api/src/server.ts' is older than output 'packages/api/tsconfig.tsbuildinfo'Recap
Today: composite projects, references between them, build mode with tsc dash b, and incremental builds. Next, and last: publishing a typed package, and a look back at the whole series.