Skip to content
advanced Phase 13 · TypeScript Build & Tooling

Monorepo with TypeScript

Set up TypeScript monorepos with project references and workspace configs.

1h 15m
0 problems
Topic Progress 0%

Workspace Layout and Shared Config

Workspace Layout and Shared Config

A TypeScript monorepo is a workspace plus a config hierarchy:

package.json        # workspaces: ["packages/*"]
tsconfig.base.json  # strict, target, shared paths
packages/core/tsconfig.json   # extends base, references: [...]
packages/web/tsconfig.json

Workspaces (npm, yarn, or pnpm) symlink package names into each other's node_modules, so import { fmt } from "@acme/core" resolves to source during development. extends gives every package the same strictness without copy-paste; each package then adds only rootDir, outDir, and its own references. Keep a single hoisted @types/node — two copies produce phantom {} mismatches between packages.

Building with Project References

Building with Project References

Mark leaf packages "composite": true and declare dependencies as references:

// packages/web/tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": { "outDir": "dist", "rootDir": "src" },
  "references": [{ "path": "../core" }]
}

tsc -b walks the graph: it builds core first, emits declarations, then typechecks web against those .d.ts files instead of re-reading core sources. The incremental state lives in .tsbuildinfo, so a no-op build is near-instant — which is what makes running tsc -b in pre-commit hooks and CI practical.

Pitfalls and Interview Angles

Pitfalls and Interview Angles

Duplicate dependencies. If web installs its own React while ui installs another, two copies load and hooks break with "Invalid hook call". Enforce one version with workspace ranges ("@acme/core": "*") and a check like npm ls react.

Stale declarations. Editing core without rebuilding leaves core/dist behind; tsc -b fixes ordering, but bundlers pointed at dist still show old code.

Publishing. Reference consumers import built output, so publishable packages need files, exports, and a prepublish build.

Interview: explain why references use .d.ts outputs, how tsc -b decides a project is up to date, and how you would detect a cycle between two references.

Quiz

1. What does tsconfig "extends" actually do?

Question 1 options

2. Why does tsc -b use each reference's declaration output instead of its sources?

Question 2 options

3. What is the usual cause of React's "Invalid hook call" inside a monorepo?

Question 3 options

Flashcards

Question

Workspaces

Answer

package.json workspaces field that links local packages into node_modules for cross-package imports.

Question

tsconfig.base.json

Answer

Shared strict compiler options; packages extend it and add only rootDir/outDir/references.

Question

Project reference

Answer

A path dependency telling tsc -b which projects to build first.

Question

.tsbuildinfo

Answer

Incremental state file; its presence lets tsc -b skip projects that are already up to date.

Revision Notes

Key Takeaways

  • 1. Workspace linking and config extends solve different problems — resolution and strictness
  • 2. tsc -b plus composite gives incremental typechecking, not bundling
  • 3. One dependency graph means one version of shared peers like React and @types

Interview Tips

  • • Describe the build order tsc -b derives and what happens when a reference cycle exists
  • • Say how you would prove there is only one React copy in the tree

Cheat Sheet

workspaces link packages; extends shares strict options; composite + references → tsc -b builds via .d.ts + .tsbuildinfo; keep single React/@types copies.

Continue Learning