How to Use TypeScript in a Monorepo Setup

A monorepo setup allows you to manage multiple packages or projects in a single repository. With TypeScript, this setup is especially powerful for sharing types, interfaces, and even utilities across different packages. This guide will walk you through how to set up TypeScript in a monorepo environment.

1. Setting Up the Monorepo

To set up a monorepo, you can use tools like npm workspaces or yarn workspaces. These tools allow you to manage multiple packages in the same repository and make it easy to share code across projects.

1.1 Initializing a Monorepo

First, create a new folder for your monorepo and initialize it with npm or yarn.

mkdir my-monorepo
cd my-monorepo
npm init -y

Then, configure workspaces in your package.json:

{
  "name": "my-monorepo",
  "version": "1.0.0",
  "private": true,
  "workspaces": [
    "packages/*"
  ]
}

This setup tells npm or yarn that all the packages will live inside the packages folder.

2. Adding Packages to the Monorepo

Create two packages in your monorepo. For this example, we'll create a shared package for reusable code and a web-app package for a frontend application.

mkdir -p packages/shared
mkdir -p packages/web-app

Inside each package, initialize a package.json:

cd packages/shared
npm init -y

cd ../web-app
npm init -y

3. Adding TypeScript to the Monorepo

Next, we'll set up TypeScript. Install TypeScript and the necessary dependencies at the root of your monorepo:

npm install typescript --save-dev

Create a root-level tsconfig.json to define the TypeScript configuration for the entire monorepo:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "*": ["packages/*/src"]
    },
    "composite": true,
    "declaration": true,
    "outDir": "dist",
    "rootDir": ".",
    "skipLibCheck": true,
    "module": "ESNext",
    "target": "ES6",
    "moduleResolution": "node"
  },
  "include": ["packages/*"]
}

The key here is the paths option, which allows TypeScript to understand imports from different packages in the monorepo.

4. Configuring TypeScript in Each Package

Each package needs its own tsconfig.json to work properly in the monorepo. Here's an example configuration for the shared package:

{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "rootDir": "src",
    "outDir": "dist"
  },
  "include": ["src"]
}

And for the web-app package:

{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "rootDir": "src",
    "outDir": "dist"
  },
  "include": ["src"]
}

Now, TypeScript can be used in each package, and the configurations are shared from the root tsconfig.json.

5. Adding TypeScript Code to the Packages

Let's add some sample TypeScript code to both packages. In the shared package, create a src folder and add a TypeScript file:

mkdir -p packages/shared/src
touch packages/shared/src/index.ts

In index.ts, export a simple function:

export const greet = (name: string): string => {
  return `Hello, ${name}!`;
}

In the web-app package, create a src folder and an index.ts file:

mkdir -p packages/web-app/src
touch packages/web-app/src/index.ts

Now, import the shared function:

import { greet } from 'shared';

console.log(greet('TypeScript Monorepo'));

6. Building the Monorepo

To compile all TypeScript code in the monorepo, we need to use the TypeScript compiler. At the root of the monorepo, run:

npx tsc --build

This command will compile all the packages by following their respective tsconfig.json files.

Conclusion

In this guide, we covered how to set up and use TypeScript in a monorepo. By organizing your code in a monorepo structure, you can easily share code across multiple packages, making your development process more efficient. With TypeScript’s strong typing and project references, this setup is perfect for large-scale applications or shared libraries.