← All posts

TypeScript path aliases in monorepos: Astro, React, and shared types

baseUrl, paths, and project references: @shared/types instead of ../../../ and silent build failures without workspace:*.

Contents

In brief

Monorepos break on ../../../packages/types chains. The CitizenApp author configures TypeScript path aliases for Astro, React 19, and a shared @shared/types package — readable imports and safer refactors.

What happened

Three approaches to shared types: relative paths (fragile), npm publish (overkill internally), path aliases (Goldilocks). Structure: packages/types/, apps in apps/web (Astro), apps/dashboard (React), root tsconfig.json.

The types package compiles separately with declaration: true, moduleResolution: bundler. package.json exports points to ./dist/index.d.ts — no ambiguity for consumers.

Root config sets baseUrl: "." and paths: { "@shared/*": ["packages/*/src"] }. Project references link workspaces as separate compilation units — faster incremental builds, fewer cycles. Each app extends the root and references packages/types.

Imports become import { User } from '@shared/types' — IDE autocomplete, moves update paths. FastAPI ignores TS aliases; generate Python types via datamodel-code-generator with TS as source of truth.

Why it matters

Without workspace:* in app dependencies, builds fail with "cannot find module" — sometimes silently on CI. Build order: compile packages/types first — explicit in pnpm / Turborepo.

Another gotcha: mismatched moduleResolution locally (nodeNext) vs Vercel (bundler) — paths resolve differently. For modern monorepos, use bundler consistently.

In practice

  1. Extract shared types to packages/types with exports and .d.ts.
  2. Configure root tsconfig.json with baseUrl, paths, and references.
  3. Each app: extends plus references to the types package.
  4. In app package.json: "@shared/types": "workspace:*".
  5. Build the types package first in CI.
  6. Unify moduleResolution: bundler everywhere.

Takeaway

Thirty minutes on correct tsconfig saves hours on imports and refactors. @shared/types instantly signals shared code. Step-by-step configs are in the original on Dev.to.