How to Share TypeScript Types Between Node.js Projects using a Monorepo and Yarn Workspaces

This post guides you through using monorepos and Yarn workspaces for effective type sharing in Node.js projects. Learn to set up and link packages for consistent code quality and simplified project maintenance.

11 min read

Working with multiple Node.js applications in TypeScript often presents a unique challenge: how to efficiently and effectively share types across different parts of your project.

This is particularly crucial during a transition from JavaScript to TypeScript, where consistency in types is key to high-quality code and error prevention.

In this guide, we’ll dive into an elegant solution for this challenge using a monorepo structure and Yarn workspaces.

Initialize a New Repository

Start by creating a new repository to house your entire project. This will serve as the foundation for implementing a monorepo structure.

A monorepo is a version control repository that holds multiple projects, allowing you to manage them collectively.

Set up Yarn

Yarn is a fast and reliable dependency management tool that supports workspaces, making it an excellent choice for managing a monorepo. If you haven’t already, install Yarn by running:

npm install -g yarnCode language: Bash (bash)

Then, initialize Yarn in your project:

yarn init -yCode language: Bash (bash)

This will generate a package.json file for your monorepo.

Create Package Structure

Let’s set up our project structure with a practical example. Imagine we are building a platform that includes a web application, an API, and a set of shared TypeScript types.

We will create directories for each package as follows:

packages/
  api/
    src/
    package.json
    ...
  web/
    src/
    package.json
    ...
  shared-types/
    src/
    package.json
    ...
package.json
Code language: Bash (bash)

Each package directory should contain its own package.json file, specifying its dependencies, scripts, and other relevant information.

Update Root Package.json

Open the package.json file in the root of your project and modify it to include an array of packages and set private to true. This ensures that your monorepo packages are not accidentally published to the npm registry:

{
  "name": "your-monorepo-name",
  "version": "1.0.0",
  "private": true,
  "workspaces": [
    "packages/*"
  ],
  "scripts": {
    "start": "your-start-script",
    "test": "your-test-script"
  }
}
Code language: JSON / JSON with Comments (json)

Replace "your-monorepo-name", "your-start-script", and "your-test-script" with your actual monorepo name and scripts.

Define Typescript Types

Inside the shared-types package, define the TypeScript types you want to share. For example, create a file named types.ts with the following content:

// packages/shared-types/src/index.ts
export interface User {
  id: string;
  username: string;
  email: string;
}
Code language: TypeScript (typescript)

Build Shared-Types Package

Before linking the shared-types package to other packages, build it to ensure TypeScript types are generated. Add a build script to the shared-types/package.json file:

{
  "name": "shared-types",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "files": [
    "dist"
  ],
  "scripts": {
    "build": "tsc",
    "build:watch": "tsc --watch "
  },
  "devDependencies": {
    "typescript": "^5.3.3"
  }
}Code language: JSON / JSON with Comments (json)

Run the build script:

cd packages/shared-types
yarn build

This will compile the TypeScript files and generate the necessary declarations.

Alternatively, you can also run the build script in watch mode where it will auto-detect file changes and rebuild the package on the go:

cd packages/shared-types
yarn build:watch

Link Shared-Types Package to Web Package

Now, navigate to the web package and link the shared-types package:

cd packages/web
yarn add link:/../shared-typesCode language: Bash (bash)

This command establishes a symbolic link between the web package and the shared-types package, allowing the web application to access the shared types seamlessly.

Link Shared-Types Package to API Package

Repeat the linking process for the API package. Navigate to the api package and run:

cd packages/api
yarn add link:/../shared-typesCode language: Bash (bash)

This links the api package to the shared-types package, enabling it to also leverage the shared TypeScript types.

Example: Using Shared Types in Web and API Packages

Now, let’s see how you can use the User interface from the shared-types package in both the web and api packages.

In the web package, you can import the User interface like this:

// packages/web/src/app.ts
import { User } from 'shared-types';

const user: User = {
  id: '123',
  username: 'john_doe',
  email: 'john.doe@example.com',
};Code language: TypeScript (typescript)

Similarly, in the api package:

// packages/api/src/controllers/userController.ts
import { User } from 'shared-types';

const getUserById = (userId: string): User => {
  // Logic to fetch user by userId
  return {
    id: userId,
    username: 'retrieved_username',
    email: 'retrieved_user@example.com',
  };
};Code language: TypeScript (typescript)

Conclusion

Adopting a monorepo structure and leveraging Yarn workspaces for your TypeScript-based Node.js applications brings numerous benefits.

It simplifies type sharing, fosters consistent code quality, and enhances overall project maintainability.

With this guide, you’re well-equipped to streamline your development process. Embark on this journey towards a more organized and efficient coding experience. Happy coding!