Ts References
## Introduction to TypeScript Project References
In large-scale TypeScript applications, keeping all source code in a single monolithic project can lead to slow compilation times, high memory usage, and poor code organization.
To address these challenges, TypeScript 3.0 introduced **Project References**. This feature allows you to structure a TypeScript codebase into smaller, independent projects. By breaking down a monolith, you can achieve:
* **Incremental Builds:** TypeScript only rebuilds modified projects rather than recompiling the entire codebase.
* **Logical Separation:** Clear boundaries between different layers of your application (e.g., frontend, backend, shared utilities).
* **Faster Compilation:** Improved developer experience with faster IDE feedback and quicker build times.
---
## How Project References Work
A project reference is a way for one TypeScript project to depend on another. This relationship is declared in the `tsconfig.json` file using the `references` property.
When you reference another project:
1. Importing modules from the referenced project will resolve to its generated declaration files (`.d.ts`) rather than its raw `.ts` source files.
2. The TypeScript compiler can build the referenced projects automatically when you build the main project using the build mode flag (`--build` or `-b`).
---
## Configuration and Syntax
To enable project references, you need to configure the `tsconfig.json` files of both the referencing (dependent) project and the referenced (dependency) project.
### 1. The Referenced Project (Dependency)
In the project being referenced, you must set the `composite` flag to `true` in its `tsconfig.json`. This tells TypeScript that this project can be built and referenced by other projects.
```json
{
"compilerOptions": {
"composite": true, // Required for project references
"declaration": true, // Required to generate .d.ts files
"outDir": "./dist",
"rootDir": "./src"
}
}
```
### 2. The Referencing Project (Consumer)
In the project that depends on the other, you specify the path to the dependency's directory (where its `tsconfig.json` is located) using the `references` array.
```json
{
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"references": [
{ "path": "../shared-utils" } // Path to the referenced project's directory
]
}
```
---
## Code Example: A Multi-Project Structure
Let's look at a practical example of a monorepo containing a `shared` utility library and an `app` that consumes it.
### Directory Structure
```text
my-project/
βββ shared/
β βββ src/
β β βββ index.ts
β βββ tsconfig.json
βββ app/
β βββ src/
β β βββ main.ts
β βββ tsconfig.json
```
### 1. The `shared` Project
**`shared/src/index.ts`**
```typescript
// A simple utility function to format strings
export function formatMessage(msg: string): string {
return `: ${msg}`;
}
```
**`shared/tsconfig.json`**
```json
{
"compilerOptions": {
"target": "es2020",
"module": "commonjs",
"composite": true, // Enables project references
"declaration": true, // Generates .d.ts files for consumers
"outDir": "./dist",
"rootDir": "./src",
"strict": true
}
}
```
### 2. The `app` Project
**`app/src/main.ts`**
```typescript
// Import the utility function from the referenced shared project
import { formatMessage } from '../../shared/src/index';
const output = formatMessage("Application started successfully!");
console.log(output);
```
**`app/tsconfig.json`**
```json
{
"compilerOptions": {
"target": "es2020",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true
},
"references": [
// Reference the shared project
{ "path": "../shared" }
]
}
```
---
## Building the Project
To compile projects that use references, you must use the TypeScript compiler's build mode flag (`--build` or `-b`).
Run the following command from your terminal in the `app` directory:
```bash
tsc -b
```
### What happens under the hood?
1. TypeScript checks if the `shared` project has already been built.
2. If `shared` has changed or hasn't been built yet, TypeScript compiles `shared` first and generates its `.d.ts` files in `shared/dist`.
3. TypeScript then compiles `app`, using the type definitions generated from `shared`.
To clean up build outputs and start fresh, you can run:
```bash
tsc -b --clean
```
---
## Key Considerations & Best Practices
* **The `composite` Constraint:** Any project referenced by another *must* have `"composite": true` enabled. This setting enforces certain constraints, such as requiring `declaration` to be true and ensuring all files are explicitly accounted for (via `include` or `files`).
* **No Circular Dependencies:** Project references must form a Directed Acyclic Graph (DAG). Project A cannot reference Project B if Project B also references Project A.
* **Pre-building in IDEs:** Modern IDEs (like VS Code) support project references out of the box. However, if you see import errors in your editor, running `tsc -b` once at the root level will generate the necessary `.d.ts` files and resolve the errors.
YouTip