Монорепозиторий с TypeScript: организация кода и лучшие практики

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Монорепозиторий с TypeScript: эффективная организация кода для масштабируемых проектов

Разработка современных приложений часто требует разделения кода на несколько независимых модулей или пакетов. При этом возникает дилемма: хранить каждый пакет в отдельном репозитории (полирепозиторий) или объединить все в одном (монорепозиторий). Для проектов на TypeScript монорепозиторий становится особенно привлекательным решением, обеспечивая единообразие типов, упрощение рефакторинга и централизованное управление зависимостями. В этой статье мы разберем, как организовать монорепозиторий с TypeScript, настроить его и эффективно использовать в разработке.

Что такое монорепозиторий и зачем он нужен TypeScript?

Монорепозиторий (monorepo) — это подход, при котором код нескольких проектов (пакетов, библиотек, микросервисов) хранится в одном репозитории. В контексте TypeScript это особенно полезно, так как:

  • Общие типы и интерфейсы: Вы можете определить общие типы в одном пакете и переиспользовать их во всех остальных, избегая дублирования и рассинхронизации.
  • Единая конфигурация: tsconfig.json, линтеры и форматтеры настраиваются один раз для всего проекта.
  • Упрощенный рефакторинг: Изменение общего кода или интерфейса сразу отражается на всех зависимых пакетах, а TypeScript подскажет о несоответствиях.
  • Атомарные коммиты: Изменения, затрагивающие несколько пакетов, можно закоммитить и запушить одной операцией.

Однако монорепозиторий требует правильной организации, иначе он превращается в хаос. Ключевые инструменты для работы с монорепозиториями в TypeScript — это npm/yarn/pnpm workspaces и TypeScript Project References.

Структура монорепозитория с TypeScript

Рассмотрим типичную структуру монорепозитория для веб-приложения, состоящего из фронтенда, бэкенда и общей библиотеки утилит:

my-monorepo/
├── packages/
│   ├── shared/          # Общие типы, утилиты, константы
│   │   ├── src/
│   │   │   └── index.ts
│   │   ├── tsconfig.json
│   │   └── package.json
│   ├── backend/         # Серверное приложение (например, Express)
│   │   ├── src/
│   │   │   └── app.ts
│   │   ├── tsconfig.json
│   │   └── package.json
│   └── frontend/        # Клиентское приложение (например, React)
│       ├── src/
│       │   └── App.tsx
│       ├── tsconfig.json
│       └── package.json
├── tsconfig.base.json   # Базовый конфигурационный файл TypeScript
├── package.json         # Корневой package.json (workspaces)
└── .gitignore

В этой структуре:

  • packages/* — директория, содержащая все пакеты проекта.
  • tsconfig.base.json — общая конфигурация TypeScript, от которой наследуются все пакеты.
  • package.json в корне объявляет рабочие пространства (workspaces).

Настройка монорепозитория: пошаговое руководство

1. Инициализация корневого проекта и настройка Workspaces

Создайте корневую директорию и файл package.json:

{
  "name": "my-monorepo",
  "private": true,
  "workspaces": [
    "packages/*"
  ],
  "scripts": {
    "build": "npm run build --workspaces",
    "lint": "eslint packages/*/src --ext .ts,.tsx",
    "test": "jest"
  }
}

Параметр "private": true предотвращает случайную публикацию корневого пакета. "workspaces": ["packages/*"] указывает npm, где искать дочерние пакеты.

2. Создание базовой конфигурации TypeScript

Файл tsconfig.base.json в корне проекта:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "node",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "composite": true
  },
  "exclude": ["node_modules", "dist"]
}

Ключевые опции: "composite": true — обязательна для использования Project References, "declaration": true — генерирует файлы .d.ts для переиспользования типов между пакетами.

3. Конфигурация пакетов с Project References

Каждый пакет должен иметь свой tsconfig.json, который наследует базовую конфигурацию и указывает свои зависимости. Пример для пакета shared:

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

Пример для пакета backend, который зависит от shared:

// packages/backend/tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "references": [
    { "path": "../shared" }
  ],
  "include": ["src"]
}

В references перечисляются пути к пакетам, от которых зависит текущий. TypeScript будет следить за изменениями в этих пакетах и пересобирать их при необходимости.

4. Настройка package.json в пакетах

Важно правильно указать зависимости между пакетами. В packages/backend/package.json:

{
  "name": "@my-monorepo/backend",
  "version": "1.0.0",
  "scripts": {
    "build": "tsc -b",
    "start": "node dist/app.js"
  },
  "dependencies": {
    "@my-monorepo/shared": "1.0.0                

Блоги

Рекомендаций по книгам