# TypeScript Project References and Monorepos

As TypeScript projects grow, compiling them can become slow. **Project References** allow you to structure your TypeScript programs into smaller pieces (sub-projects). This improves build times, enforces logical separation between components, and organizes code better.

This feature is the backbone of TypeScript Monorepos.

## 1. Why use Project References?

*   **Faster Builds:** TypeScript can check which sub-projects have changed and only rebuild those, rather than recompiling the entire codebase.
*   **Logical Separation:** You can enforce boundaries. For example, your `frontend` code shouldn't import `backend` code, or your `utils` library shouldn't import `app` logic.
*   **Editor Performance:** IDEs can load smaller projects faster.

## 2. Enabling Project References

To turn a folder into a referenced project, you need to enable the `composite` setting in its `tsconfig.json`.

### The `composite` flag

Setting `"composite": true` ensures that TypeScript can quickly determine where to find the outputs of the referenced project.

```json
// packages/shared/tsconfig.json
{
  "compilerOptions": {
    "composite": true,
    "declaration": true,  /* Required for composite projects */
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"]
}
```

## 3. Structuring a Monorepo

Imagine a directory structure like this:

```text
my-monorepo/
  tsconfig.json       (Root config)
  packages/
    shared/
      tsconfig.json
      src/
    backend/
      tsconfig.json
      src/
    frontend/
      tsconfig.json
      src/
```

### The Root `tsconfig.json`

The root config usually acts as a "solution" file. It doesn't contain source code itself but references the sub-projects.

```json
// tsconfig.json (Root)
{
  "files": [],
  "references": [
    { "path": "./packages/shared" },
    { "path": "./packages/backend" },
    { "path": "./packages/frontend" }
  ]
}
```

### Dependent Projects

If `backend` depends on `shared`, you must declare this in `backend/tsconfig.json`.

```json
// packages/backend/tsconfig.json
{
  "compilerOptions": {
    "composite": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "references": [
    { "path": "../shared" }
  ],
  "include": ["src/**/*"]
}
```

Now, inside `backend/src/index.ts`, you can import from `shared`. TypeScript knows to look at the *output* (`.d.ts` files) of `shared` rather than recompiling its source code every time `backend` is built.

## 4. Build Mode (`tsc -b`)

To build a project with references, you cannot use the standard `tsc` command. You must use the **Build Mode** flag: `-b` or `--build`.

```bash
# Build the entire solution from the root
tsc -b

# Build a specific package and its dependencies
tsc -b packages/backend

# Clean build artifacts
tsc -b --clean
```

`tsc -b` is smarter than `tsc`. It checks modification timestamps and configuration files to decide what needs to be rebuilt.

## 5. Best Practices

1.  **Use Path Aliases:** Often combined with `paths` in `compilerOptions` so you can import like `import { util } from '@my-app/shared'` instead of `../../shared/dist`.
2.  **Output Structure:** Ensure `outDir` is configured correctly so compiled files don't clutter source folders.
3.  **Declaration Maps:** Enable `"declarationMap": true`. This allows "Go to Definition" in your IDE to jump to the original `.ts` source file in the referenced project, rather than the generated `.d.ts` file.

[[programming/javascript/typescript/typescript]]
[[programming/javascript/typescript/tsconfig]]