YouTip LogoYouTip

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.
← Ts Design PatternsTs Covariance β†’