Git workflow для TypeScript: как не сойти с ума в типизированном коде

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

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

Начать курс

Git workflow для TypeScript: как перестать бояться merge conflict

Если вы когда-нибудь пытались смержить две ветки, где одна меняла интерфейс User, а другая — добавляла новый тип в тот же самый модуль, вы знаете, что такое экзистенциальный кризис разработчика. TypeScript даёт нам мощную систему типов, но без правильного Git workflow эта мощь превращается в головную боль.

В этой статье мы разберём оптимальный Git workflow для TypeScript-проектов — от настройки веток до автоматической проверки типов при каждом коммите. Никакой воды, только то, что реально работает в бою.

Почему TypeScript требует особого подхода к Git?

TypeScript — это не просто «JavaScript с типами». Это язык, где каждое изменение может затронуть десятки файлов через interface, type и дженерики. Одна неверная правка в базовом типе — и весь проект перестаёт компилироваться.

Поэтому стандартный git workflow для TypeScript должен включать:

  • Обязательную проверку типов перед коммитом
  • Линтер, который не даёт закоммитить неформатированный код
  • Чёткую стратегию ветвления, чтобы merge conflict не ломал типы
  • CI/CD, который перепроверяет всё на сервере

Давайте разберём каждый пункт по порядку.

1. Выбираем стратегию ветвления: Git Flow или Trunk-based?

Для TypeScript-проектов я рекомендую Trunk-based development с короткоживущими ветками. Почему? Потому что TypeScript-код — это, по сути, «сырой» код, который нужно быстро проверять и деплоить. Долгие ветки приводят к тому, что типы «разъезжаются».

Базовая структура веток

main        — стабильная версия, всегда готова к деплою
develop     — основная ветка разработки
feature/*   — новые фичи (живут 1-2 дня)
fix/*       — багфиксы
release/*   — подготовка релиза (опционально)

Пример для TypeScript-проекта:

git checkout -b feature/add-user-validation
# работаем...
git commit -m "feat: add email validation for User type"
git push origin feature/add-user-validation
# создаём PR → merge в develop

Важно: никогда не делайте rebase на main, если в проекте есть общие типы. Используйте merge — он сохраняет историю и не ломает зависимости.

2. Git Hooks: защита от дурака (и от себя)

Самый частый сценарий: вы забыли запустить tsc, закоммитили, запушили, а CI упал. Чтобы этого избежать, настраиваем pre-commit hooks.

Установка Husky + lint-staged

Husky — это инструмент для управления Git hooks. lint-staged — запускает команды только на изменённых файлах.

npm install --save-dev husky lint-staged
npx husky init

Создаём файл .husky/pre-commit:

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged

В package.json добавляем:

{
  "lint-staged": {
    "*.ts": [
      "eslint --fix",
      "prettier --write",
      "tsc --noEmit"
    ]
  }
}

Теперь перед каждым коммитом:

  1. Запускается ESLint с автофиксом
  2. Форматируется код через Prettier
  3. Проверяется сборка TypeScript (tsc --noEmit)

Если хоть один шаг упадёт — коммит не пройдёт. Жестоко, но эффективно.

3. Правильные commit message: conventional commits

TypeScript-проекты выигрывают от conventional commits, потому что:

  • Автоматически генерируется changelog
  • Легко понять, какие изменения ломают типы
  • Можно триггерить CI только на определённые коммиты

Примеры:

feat: add UserRole enum and validation
fix: correct type inference in useAuth hook
refactor: extract UserService from UserController
chore: update TypeScript to 5.4

Обратите внимание: breaking change в TypeScript — это всегда изменение публичного API или типа. Если вы удалили поле из интерфейса — это BREAKING CHANGE.

git commit -m "feat!: remove deprecated email field from User interface"

4. Code Review в TypeScript: на что обращать внимание

Когда вы открываете Pull Request, проверяйте не только логику, но и:

  • Типы: не появился ли any? Не слишком ли сложный дженерик?
  • Импорты: нет ли циклических зависимостей?
  • Совместимость: ломает ли этот PR существующие типы?

Совет: добавьте в CI проверку API Extractor, чтобы видеть, как изменился публичный API библиотеки.

Пример проверки на сломанные типы

В CI можно добавить шаг:

npm run typecheck
# или
npx tsc --noEmit --project tsconfig.json

Если проект большой — используйте TypeScript Project References и проверяйте только изменённые модули.

5. CI/CD для TypeScript: быстрая обратная связь

После мержа в develop или main, ваш CI должен делать:

  1. npm ci — чистая установка зависимостей
  2. npx tsc --noEmit — проверка типов
  3. npx eslint . — линтинг
  4. npm test — юнит-тесты
  5. npm run build — сборка

Пример для GitHub Actions:

name: TypeScript CI

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version: 20

    - run: npm ci
    - run: npx tsc --noEmit
    - run: npx eslint .
    - run: npm test
    - run: npm run build

Этот workflow отсекает 90% проблем ещё до того, как код попадёт в main.

6. Работа с merge conflict в TypeScript

Типичный конфликт в TypeScript — это когда две ветки добавляют поля в один интерфейс. Вот как это выглядит:

<<<                

Блоги

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