$ sudo teach IT
МОДУЛЬ 1 · УРОК 5

Настройка tsconfig.json

Разбираемся в файле конфигурации TypeScript. Узнаём, какие опции доступны и как настроить компилятор под себя.

⏱ ~30 минут🎓 Для новичков🛠 Практика

1. Зачем нужен tsconfig.json

TypeScript — это не просто язык с типами. Это целая экосистема с мощным компилятором, который можно настраивать под тысячи различных сценариев. И главным инструментом управления поведением компилятора является файл tsconfig.json.

Представьте ситуацию: вы создаёте новый проект на TypeScript. Вы запускаете компилятор командой tsc myFile.ts, и он успешно транспилирует ваш файл в JavaScript. Но что если у вас десятки файлов? Что если вы хотите, чтобы результатом был ES2020-код вместо ES5? Что если вам нужна строгая проверка типов? Что если вы хотите генерировать файлы declarations для других разработчиков?

Конечно, всё это можно задать через флаги командной строки. Но представляете, что вам каждый раз приходится печатать десяток параметров? Это неудобно, ошибочно и не масштабируется. Именно для этого существует tsconfig.json — файл, который хранит все настройки компилятора в одном месте.

Что делает tsconfig.json:
  • Определяет, какие файлы компилировать (и какие игнорировать)
  • Задаёт целевую версию JavaScript, в которую транспилируется код
  • Управляет уровнем строгости проверки типов
  • Настраивает систему модулей
  • Указывает, куда сохранять скомпилированные файлы
  • Определяет, включать ли генерацию declaration-файлов
  • Управляет генерацией source maps
  • Задаёт доступные встроенные типы (lib)
  • Влияет на поведение IDE (автодополнение, переходы, рефакторинг)

Важно понимать, что tsconfig.json — это не просто файл настроек для компилятора. Это контракт для всего проекта. Когда вы открываете проект в Visual Studio Code, именно tsconfig.json определяет, какие подсказки будет показывать редактор, какие ошибки подчёркивать, какие переходы будут доступны. TypeScript Language Server, который работает под капотом VS Code, читает tsconfig.json и строит на его основе всю модель проекта.

Без tsconfig.json TypeScript всё равно работает. Компилятор использует значения по умолчанию, которые обычно слишком мягкими для серьёзной работы. Например, по умолчанию strict mode отключён, target установлен в ES3 (самую древнюю версию JavaScript), а система модулей использует устаревший CommonJS. Если вы хотите работать профессионально — tsconfig.json обязателен.

Почему tsconfig.json важен для команды:

В командной разработке каждый разработчик должен использовать одни и те же настройки компилятора. Если один разработчик компилирует код в ES5, а другой — в ES2020, результат может быть непредсказуемым. tsconfig.json хранится в репозитории и версионируется через Git, поэтому все участники команды работают с одинаковыми настройками.

Кроме того, tsconfig.json используется не только компилятором tsc. Многие инструменты сборки — Webpack, Vite, Rollup, esbuild — читают tsconfig.json для определения параметров компиляции TypeScript. Тестовые фреймворки — Jest, Vitest, Mocha — также ориентируются на настройки из этого файла. Поэтому правильная конфигурация tsconfig.json критически важна для всей инфраструктуры проекта.

Давайте разберём, как создаётся этот файл и какие опции в нём доступны. Вы увидите, что настройка TypeScript — это не страшно и даже увлекательно. Каждая опция существует не просто так, и понимание их назначения делает вас более эффективным разработчиком.

2. Создаём tsconfig.json

Существует несколько способов создать файл tsconfig.json. Давайте рассмотрим каждый из них и поймём, какой подходит для какого случая.

Способ 1: Автоматическая генерация через tsc --init

Самый быстрый способ начать — воспользоваться встроенной командой компилятора:

tsc --init

Эта команда создаёт файл tsconfig.json в текущей директории с набором рекомендуемых настроек и подробными комментариями к каждой опции. Комментарии начинаются с символа «//» и объясняют, зачем нужна каждая настройка. Это отличный способ для начинающих — вы получаете рабочую конфигурацию и можете изучать её, раскомментируя нужные опции по мере необходимости.

Результат команды tsc --init выглядит примерно так:

{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}

Как видите, автоматическая генерация создаёт файл с базовым набором опций. Раскомментированными остаются только основные: target, module, strict и esModuleInterop. Это разумный минимум для старта.

Способ 2: Создание вручную

Вы можете создать tsconfig.json вручную — просто создайте файл с таким именем в корне проекта и добавьте нужные настройки. Это хороший вариант, если вы точно знаете, что вам нужно, или если работаете по шаблону конфигурации из команды.

// tsconfig.json — минимальная конфигурация
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "strict": true,
    "outDir": "./dist"
  }
}

Способ 3: Через Visual Studio Code

Если вы используете VS Code и открываете TypeScript-файл без tsconfig.json, редактор предложит вам создать его. Это удобный способ, который генерирует базовую конфигурацию и сразу настраивает Language Server для правильной работы.

Важно: Файл tsconfig.json должен находиться в корне проекта (или в корне packages/ для монорепозитория). Компилятор ищет его, поднимаясь по дереву директорий от файла, который компилируется, до корня файловой системы. Если tsconfig.json не найден в текущей директории, TypeScript попытается найти его в родительских каталогах.

Где размещать tsconfig.json

Рекомендуемое размещение tsconfig.json зависит от структуры вашего проекта:

# Простой проект
my-project/
├── tsconfig.json        ← в корне проекта
├── src/
│   ├── index.ts
│   └── utils.ts
└── package.json

# Монорепозиторий (monorepo)
monorepo/
├── tsconfig.json        ← базовая конфигурация
├── packages/
│   ├── core/
│   │   ├── tsconfig.json   ← наследует от корневого
│   │   └── src/
│   └── utils/
│       ├── tsconfig.json   ← наследует от корневого
│       └── src/

В монорепозиториях используется механизм наследования конфигураций через опцию extends. Это позволяет вынести общие настройки в корневой tsconfig.json и переопределять только специфичные параметры в каждом пакете. Мы подробно разберём это в разделе о примерах конфигураций.

3. Основные настройки

Давайте подробно разберём каждую важную опцию tsconfig.json. Эти настройки определяют поведение компилятора и качество вашего кода.

target — Целевая версия JavaScript

Опция target определяет, в какую версию JavaScript будет транспилироваться ваш TypeScript-код. Это одна из самых важных настроек, потому что она определяет, какие фичи JavaScript будут доступны в результате компиляции.

Доступные значения для target:

{
  "compilerOptions": {
    // ES5 — поддержка старых браузеров (IE11)
    // "target": "ES5",

    // ES2015 (ES6) — стрелочные функции, let/const, классы
    // "target": "ES2015",

    // ES2017 — async/await, Object.entries, Object.values
    // "target": "ES2017",

    // ES2020 — optional chaining (?.), nullish coalescing (??)
    // "target": "ES2020",

    // ES2022 — top-level await, class fields, at() method
    // "target": "ES2022",

    // ESNext — последняя доступная версия (всегда актуальная)
    // "target": "ESNext"

    // Рекомендованное значение для современных проектов:
    "target": "ES2020"
  }
}

При выборе target учитывайте среду выполнения вашего приложения. Если это браузернаяアプリ — проверьте, какие браузеры вы поддерживаете. Если это Node.js — проверьте версию Node.js на серверах. Если вы используете современные инструменты сборки (Vite, esbuild), они могут обрабатывать транспилирацию самостоятельно, и тогда target можно поставить в ESNext.

Совет: Если вы не уверены, какой target выбрать, начните с "ES2020". Это покрывает большинство современных браузеров и Node.js 14+. Если нужна поддержка более старых окружений —откат до "ES2017" или "ES2015". Вы всегда можете изменить эту настройку позже.

module — Система модулей

Опция module определяет, какую систему модулей использовать в скомпилированном коде. Это критически важная настройка, которая влияет на то, как ваши модули будут импортироваться и экспортироваться.

{
  "compilerOptions": {
    // CommonJS — модули Node.js (require/module.exports)
    // "module": "commonjs",

    // ES2015 — нативные ES-модули (import/export)
    // "module": "ES2015",

    // ESNext — последняя версия ES-модулей
    // "module": "ESNext",

    // Node16/NodeNext — для Node.js с ESM-поддержкой
    // "module": "Node16",

    // Рекомендованное значение:
    "module": "ESNext"
  }
}

Для современных проектов рекомендуется использовать "ESNext" или "ES2020". Если вы разрабатываете для Node.js с поддержкой ESM, используйте "NodeNext". Для старых Node.js проектов подходит "commonjs".

Важно: Выбор module должен соответствовать вашей среде выполнения. Браузеры поддерживают ES-модули через <script type="module">, но не все среды одинаково хорошо работают с динамическими импортами. Node.js до версии 14 экспериментально поддерживал ESM, а стабильная поддержка появилась в 14.x. Начиная с Node.js 16, ESM поддерживается полноценно.

strict — Строгий режим

Опция strict — это зонтичная настройка, которая включает сразу несколько строгих проверок типов. Это, пожалуй, самая важная настройка для качества кода.

{
  "compilerOptions": {
    // Включает ВСЕ перечисленные ниже опции:
    "strict": true,

    // Эквивалентно записи:
    // "noImplicitAny": true,
    // "strictNullChecks": true,
    // "strictFunctionTypes": true,
    // "strictBindCallApply": true,
    // "strictPropertyInitialization": true,
    // "noImplicitThis": true,
    // "useUnknownInCatchVariables": true,
    // "alwaysStrict": true
  }
}

Давайте разберём каждую составляющую strict-режима:

noImplicitAny — запрещает неявный тип any. Если вы объявляете переменную без типа и TypeScript не может вывести тип автоматически, компилятор выдаст ошибку:

// ❌ Ошибка: Parameter 'x' implicitly has an 'any' type
function double(x) {
  return x * 2;
}

// ✅ Правильно: явно указан тип
function double(x: number): number {
  return x * 2;
}

strictNullChecks — включает строгую проверку null и undefined. По умолчанию null и undefined можно присвоить любой переменной. С этой опцией типы null и undefined становятся отдельными типами:

// ❌ Ошибка: Type 'null' is not assignable to type 'string'
const name: string = null;

// ✅ Правильно: явно указываем, что переменная может быть null
const name: string | null = null;

// ✅ Проверка перед использованием
function greet(name: string | null): string {
  if (name === null) {
    return 'Привет, незнакомец!';
  }
  return `Привет, ${name}!`;
}

strictFunctionTypes — включает строгую проверку типов функций. Функции проверяются по contravariance (контравариантность) для параметров:

function processAnimal(animal: { name: string; legs: number }) {
  console.log(animal.name, animal.legs);
}

// С strictFunctionTypes это будет ошибкой:
// const process: typeof processAnimal = (dog: { name: string }) => console.log(dog.name);

strictBindCallApply — обеспечивает строгую проверку типов для методов bind, call и apply:

function add(a: number, b: number): number {
  return a + b;
}

// С strictBindCallApply типы сохраняются:
const add5 = add.bind(null, 5);
const result: number = add5(3); // ✅ result: number

strictPropertyInitialization — требует, чтобы все свойства класса инициализировались в конструкторе:

class User {
  name: string;      // ❌ Ошибка: Property 'name' has no initializer
  age: number = 0;   // ✅ Инициализировано
  email?: string;     // ✅ Опциональное

  constructor(name: string) {
    this.name = name; // ✅ Инициализируется в конструкторе
  }
}

outDir — Папка вывода

Опция outDir указывает, куда компилятор должен помещать скомпилированные JavaScript-файлы. Это помогает разделить исходный код и результат компиляции.

// tsconfig.json
{
  "compilerOptions": {
    "outDir": "./dist"
  }
}

// Структура проекта:
my-project/
├── tsconfig.json
├── src/
│   ├── index.ts        ← исходный файл
│   └── utils/
│       └── math.ts     ← исходный файл
└── dist/               ← сюда компилируется
    ├── index.js
    └── utils/
        └── math.js

Структура директорий в outDir повторяет структуру исходных файлов. Если исходный файл находится в src/utils/math.ts, скомпилированный файл окажется в dist/utils/math.js.

rootDir — Корневая папка исходников

Опция rootDir указывает корневую директорию для входных файлов. Компилятор использует её для вычисления структуры выходных файлов.

// tsconfig.json
{
  "compilerOptions": {
    "rootDir": "./src",
    "outDir": "./dist"
  }
}

// Структура:
my-project/
├── tsconfig.json
├── src/
│   ├── index.ts
│   └── utils/
│       └── math.ts
├── tests/
│   └── math.test.ts     ← НЕ компилируется (вне rootDir)
└── dist/
    ├── index.js
    └── utils/
        └── math.js
Совет: Всегда указывайте rootDir, если исходные файлы находятся в поддиректории (например, src/). Без этого компилятор будет включать все файлы, включая конфигурационные, что приведёт к ненужной структуре в выходной директории.

include и exclude — Файлы для компиляции

Опции include и exclude позволяют точно контролировать, какие файлы компилируются, а какие игнорируются.

{
  "compilerOptions": {
    "rootDir": "./src",
    "outDir": "./dist"
  },
  // Какие файлы компилировать:
  "include": [
    "src/**/*.ts",        // Все .ts файлы в src и поддиректориях
    "src/**/*.tsx"        // Все .tsx файлы (React-компоненты)
  ],
  // Какие файлы исключить:
  "exclude": [
    "node_modules",       // Зависимости
    "dist",               // Выходная директория
    "tests",              // Тесты
    "**/*.test.ts",       // Файлы тестов
    "**/*.spec.ts",       // Файлы спецификаций
    "**/*.d.ts"           // Declaration файлы
  ]
}

Глобальные паттерны в include и exclude поддерживают символы * (любые символы кроме /) и ** (любые символы включая /). Это позволяет гибко настраивать набор компилируемых файлов.

Если опции include и exclude не указаны, компилятор компилирует все TypeScript-файлы в текущей директории и поддиректориях, кроме node_modules и bower_components.

lib — Библиотеки типов

Опция lib определяет, какие встроенные типы доступны в проекте. Это позволяет контролировать доступность API, таких как Promise, Array, Map, Set и других.

{
  "compilerOptions": {
    // Только ES-типы (для Node.js):
    "lib": ["ES2020"],

    // ES + DOM (для браузеров):
    // "lib": ["ES2020", "DOM", "DOM.Iterable"],
  }
}

Для Node.js проектов обычно используют "ES2020" или "ESNext" без DOM-типов. Для браузерных проектов добавляют "DOM" и "DOM.Iterable". Это важно, потому что DOM-типы добавляют глобальные типы document, window, HTMLElement и другие.

noEmit — Отключение генерации файлов

Опция noEmit запрещает компилятору генерировать JavaScript-файлы. Компилятор всё ещё выполняет проверку типов, но не создаёт выходные файлы. Это полезно, когда вы используете другой инструмент для сборки (Webpack, Vite, esbuild).

{
  "compilerOptions": {
    "noEmit": true,
    "strict": true
  }
}

// Теперь tsc проверяет типы, но не создаёт .js файлы:
// $ tsc --noEmit
Совет: Используйте noEmit в проектах с Webpack/Vite/esbuild. Эти инструменты берут на себя транспилирацию, а TypeScript отвечает только за проверку типов. Это ускоряет процесс разработки и избегает конфликтов.

declaration — Генерация .d.ts файлов

Опция declaration включает генерацию файлов деклараций (.d.ts). Эти файлы содержат типизированные описания вашего кода и используются другими проектами, которые импортируют вашу библиотеку.

{
  "compilerOptions": {
    "declaration": true,
    "declarationDir": "./types"
  }
}

// Исходный файл: src/math.ts
export function add(a: number, b: number): number {
  return a + b;
}

// Сгенерированный файл: types/math.d.ts
export declare function add(a: number, b: number): number;

Файлы деклараций особенно важны для библиотек. Когда другой разработчик устанавливает ваш пакет через npm, он получает только JavaScript-код и файлы .d.ts. TypeScript использует .d.ts файлы для предоставления автодополнения и проверки типов в потребительском проекте.

sourceMap — Генерация Source Maps

Source maps — это файлы, которые сопоставляют скомпилированный JavaScript с исходным TypeScript-кодом. Они необходимы для отладки: когда вы ставите breakpoint в TypeScript-файле в браузере или IDE, source map позволяет отследить выполнение обратно к исходному коду.

{
  "compilerOptions": {
    "sourceMap": true,        // Генерирует отдельные .map файлы
    // "inlineSourceMap": true  // Встраивает source map в .js файл
  }
}

// Результат:
dist/
├── index.js        ← скомпилированный код
├── index.js.map    ← source map (при sourceMap: true)
└── ...
Важно: Не публикуйте source map в продакшене, если не хотите, чтобы пользователи видели ваш исходный код. Source map — это по сути миниатюрная копия вашего кода. В продакшене source map обычно генерируются, но загружаются только при необходимости.

4. Структура проекта

Правильная структура проекта — это основа поддерживаемого кода. TypeScript-проекты имеют свою специфику, связанную с тем, как компилятор ищет файлы, как генерируются выходные данные и как организована типизация.

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

my-typescript-project/
├── tsconfig.json           # Конфигурация TypeScript
├── package.json            # Метаданные проекта и зависимости
├── .gitignore              # Игнорируемые файлы
├── src/                    # Исходный код
│   ├── index.ts            # Точка входа
│   ├── types/              # Пользовательские типы
│   │   ├── index.ts        # Экспорт всех типов
│   │   ├── user.ts         # Типы пользователя
│   │   └── api.ts          # Типы API
│   ├── utils/              # Утилиты
│   │   ├── index.ts
│   │   ├── validation.ts
│   │   └── formatting.ts
│   ├── services/           # Бизнес-логика
│   │   ├── index.ts
│   │   ├── authService.ts
│   │   └── userService.ts
│   └── config/             # Конфигурация приложения
│       ├── index.ts
│       └── constants.ts
├── dist/                   # Скомпилированный код (не коммитится)
│   ├── index.js
│   └── ...
└── node_modules/           # Зависимости (не коммитится)

Организация типов

В TypeScript-проектах важно правильно организовать типы. Существует несколько подходов:

// Подход 1: Типы рядом с кодом
src/
├── models/
│   ├── User.ts        # Содержит интерфейс User
│   └── Post.ts        # Содержит интерфейс Post
└── services/
    ├── authService.ts # Использует типы из models
    └── userService.ts # Использует типы из models

// Подход 2: Отдельная папка для типов
src/
├── types/
│   ├── index.ts       # Единая точка экспорта
│   ├── user.ts        # Интерфейс User
│   └── api.ts         # Типы API-ответов
├── models/
│   ├── User.ts
│   └── Post.ts
└── services/
    └── userService.ts # Импортирует из types/

Barrel export (индексные файлы)

Популярный паттерн в TypeScript-проектах — использование индексных файлов (barrel exports) для удобного экспорта модулей:

// src/types/index.ts
export { User } from './user';
export { Post } from './post';
export { ApiResponse } from './api';

// src/utils/index.ts
export { validate } from './validation';
export { formatDate } from './formatting';
export { generateId } from './helpers';

// Использование в других файлах:
import { User, Post } from '../types';
import { validate, formatDate } from '../utils';
Совет: Barrel exports удобны для импорта, но могут вызвать проблемы с tree-shaking (удалением неиспользуемого кода) в некоторых сборщиках. Если производительность критична, импортируйте файлы напрямую, а barrel exports используйте только в корне пакета.

Папка declarations

Для библиотек важно правильно настроить генерацию declaration файлов. Вот как это сделать:

// tsconfig.json для библиотеки
{
  "compilerOptions": {
    "declaration": true,
    "declarationMap": true,
    "declarationDir": "./types",
    "outDir": "./dist"
  }
}

// Структура после компиляции:
my-library/
├── src/
│   ├── index.ts
│   └── utils.ts
├── dist/
│   ├── index.js
│   ├── index.d.ts         # Declaration файл
│   ├── index.d.ts.map     # Source map для declaration
│   ├── utils.js
│   └── utils.d.ts
└── package.json
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts"

5. Примеры конфигураций

Давайте рассмотрим реальные конфигурации для разных типов проектов. Каждая из них оптимизирована под конкретный сценарий использования.

Библиотека (npm-пакет)

Конфигурация для публикации библиотеки в npm. Включает генерацию declaration файлов, поддержку ESM и CommonJS, строгую проверку типов:

// tsconfig.json — Библиотека (npm-пакет)
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "bundler",

    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,

    "declaration": true,
    "declarationMap": true,
    "declarationDir": "./types",

    "sourceMap": true,
    "outDir": "./dist",
    "rootDir": "./src",

    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "isolatedModules": true,
    "verbatimModuleSyntax": true,

    "resolveJsonModule": true
  },
  "include": ["src/**/*.ts", "src/**/*.tsx"],
  "exclude": [
    "node_modules",
    "dist",
    "types",
    "**/*.test.ts",
    "**/*.spec.ts",
    "**/*.d.ts"
  ]
}

Соответствующий package.json для этой конфигурации:

// package.json
{
  "name": "my-awesome-lib",
  "version": "1.0.0",
  "main": "./dist/index.js",
  "module": "./dist/index.js",
  "types": "./types/index.d.ts",
  "exports": {
    ".": {
      "types": "./types/index.d.ts",
      "import": "./dist/index.js",
      "require": "./dist/index.js"
    }
  },
  "files": ["dist", "types"],
  "scripts": {
    "build": "tsc",
    "typecheck": "tsc --noEmit",
    "prepublishOnly": "npm run build"
  }
}

Веб-приложение (React)

Конфигурация для SPA на React. Использует noEmit, потому что сборка выполняется через Vite/Webpack:

// tsconfig.json — Веб-приложение (React)
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "jsx": "react-jsx",

    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,

    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@components/*": ["./src/components/*"],
      "@utils/*": ["./src/utils/*"],
      "@hooks/*": ["./src/hooks/*"]
    }
  },
  "include": ["src/**/*.ts", "src/**/*.tsx"],
  "exclude": ["node_modules", "dist"]
}

Node.js-приложение (сервер)

Конфигурация для серверного приложения на Node.js:

// tsconfig.json — Node.js сервер
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "Node16",
    "moduleResolution": "Node16",

    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,

    "outDir": "./dist",
    "rootDir": "./src",
    "declaration": true,
    "sourceMap": true,

    "resolveJsonModule": true,
    "isolatedModules": true
  },
  "include": ["src/**/*.ts"],
  "exclude": ["node_modules", "dist", "**/*.test.ts"]
}

Монорепозиторий (наследование конфигураций)

В монорепозиториях используется механизм extends для наследования конфигураций:

// tsconfig.base.json (в корне monorepo)
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "declaration": true,
    "sourceMap": true,
    "isolatedModules": true
  }
}

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

// packages/web/tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "noEmit": true,
    "jsx": "react-jsx",
    "lib": ["ES2020", "DOM", "DOM.Iterable"]
  },
  "include": ["src/**/*.ts", "src/**/*.tsx"]
}

Механизм extends позволяет вынести общие настройки в базовый файл и переопределять только специфичные параметры в каждом пакете. Это снижает дупликацию и упрощает поддержку конфигурации.

6. Полезные опции

Помимо основных настроек, в tsconfig.json есть множество дополнительных опций, которые могут существенно улучшить ваш рабочий процесс. Давайте разберём самые полезные из них.

noUnusedLocals и noUnusedParameters

Эти опции помогают поддерживать чистоту кода, запрещая неиспользуемые переменные и параметры функций:

{
  "compilerOptions": {
    "noUnusedLocals": true,       // Ошибка при неиспользуемых переменных
    "noUnusedParameters": true    // Ошибка при неиспользуемых параметрах
  }
}

// ❌ Ошибка: 'unused' is declared but never used
const unused = 42;

// ❌ Ошибка: 'x' is declared but its value is never read
function doSomething(x: number): number {
  return 1;
}

noImplicitReturns

Гарантирует, что все пути выполнения функции возвращают значение:

{
  "compilerOptions": {
    "noImplicitReturns": true
  }
}

// ❌ Ошибка: Not all code paths return a value
function getArea(shape: string, width: number, height: number): number {
  if (shape === 'rectangle') {
    return width * height;
  }
  // Нет return для других вариантов!
}

// ✅ Правильно:
function getArea(shape: string, width: number, height: number): number {
  if (shape === 'rectangle') {
    return width * height;
  }
  return 0; // дефолтное значение
}

noFallthroughCasesInSwitch

Запрещает пропуск break в switch-конструкциях — классическую ошибку JavaScript:

{
  "compilerOptions": {
    "noFallthroughCasesInSwitch": true
  }
}

// ❌ Ошибка: Fallthrough case in switch
function getDayType(day: string): string {
  switch (day) {
    case 'monday':
      return 'weekday';
    case 'tuesday':
      // Oops, forgot break!
    case 'wednesday':
      return 'weekday';
    default:
      return 'unknown';
  }
}

noUncheckedIndexedAccess

Добавляет undefined в тип при обращении по индексу — это заставляет вас проверять наличие элемента:

{
  "compilerOptions": {
    "noUncheckedIndexedAccess": true
  }
}

const arr: number[] = [1, 2, 3];

// С noUncheckedIndexedAccess:
const item = arr[0]; // тип: number | undefined

// ❌ Ошибка: Object is possibly 'undefined'
console.log(item.toFixed(2));

// ✅ Правильно:
if (item !== undefined) {
  console.log(item.toFixed(2));
}

exactOptionalPropertyTypes

Различает отсутствие свойства и явное присвоение undefined:

{
  "compilerOptions": {
    "exactOptionalPropertyTypes": true
  }
}

interface Config {
  name?: string;  // Может быть undefined?
  age?: number;
}

// С exactOptionalPropertyTypes:
const config1: Config = { name: "test" };           // ✅
const config2: Config = { name: undefined };         // ❌ Ошибка!
const config3: Config = {};                          // ✅

paths — Алиасы для импортов

Опция paths позволяет создавать алиасы для длинных путей импорта, делая код более читаемым:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@components/*": ["./src/components/*"],
      "@utils/*": ["./src/utils/*"],
      "@hooks/*": ["./src/hooks/*"],
      "@services/*": ["./src/services/*"]
    }
  }
}

// Вместо:
import { Button } from '../../../components/Button/Button';
import { formatDate } from '../../../utils/formatting';

// Пишем:
import { Button } from '@components/Button/Button';
import { formatDate } from '@utils/formatting';
Важно: Опция paths влияет только на проверку типов TypeScript. Для реального разрешения модулей при сборке необходимо настроить алиасы и в вашем сборщике (Webpack aliases, Vite resolve.alias и т.д.).

incremental — Инкрементальная компиляция

Опция incremental значительно ускоряет повторную компиляцию, сохраняя информацию о предыдущей сборке:

{
  "compilerOptions": {
    "incremental": true,
    "tsBuildInfoFile": "./.tsbuildinfo"
  }
}

При первой компиляции TypeScript создаёт файл .tsbuildinfo, содержащий информацию о зависимостях и состояниях файлов. При последующих компиляциях он перекомпилирует только изменённые файлы. Это особенно полезно в крупных проектах, где полная компиляция может занимать несколько минут.

resolveJsonModule — Импорт JSON-файлов

Позволяет импортировать JSON-файлы с полной проверкой типов:

{
  "compilerOptions": {
    "resolveJsonModule": true,
    "esModuleInterop": true
  }
}

// package.json
{ "name": "my-app", "version": "1.0.0" }

// index.ts
import pkg from './package.json';
console.log(pkg.name);    // ✅ TypeScript знает тип
console.log(pkg.version); // ✅ Автодополнение работает

7. Типичные ошибки

При настройке tsconfig.json разработчики часто допускают одни и те же ошибки. Давайте разберём самые распространённые из них и способы их избежать.

Ошибка 1: Отсутствие strict mode

Многие разработчики не включают strict: true, потому что это создаёт много ошибок. Но это ловкая ловушка — без strict mode TypeScript не может гарантировать безопасность вашего кода.

// ❌ Без strict mode этот код скомпилируется без ошибок:
function processUser(user) {
  console.log(user.name);  // Может упасть, если user = null
}

// ✅ С strict mode вы сразу увидите проблему:
// Parameter 'user' implicitly has an 'any' type.

Ошибка 2: Неправильный выбор module

Использование "module": "commonjs" в проекте с ES-импортами приводит к проблемам:

// ❌ Если в tsconfig.json:
// "module": "commonjs"
// но в package.json:
// "type": "module"

// Результат: Node.js не сможет запустить ваш код
// потому что CommonJS и ESM несовместимы

Ошибка 3: Забыть настроить outDir

Без outDir скомпилированные .js файлы оказываются рядом с исходными .ts файлами, что засоряет проект:

// Без outDir:
src/
├── index.ts
├── index.js      ← скомпилированный файл рядом с исходником
├── utils.ts
└── utils.js      ← ещё один скомпилированный файл

// С outDir:
src/
├── index.ts
└── utils.ts
dist/
├── index.js
└── utils.js

Ошибка 4: Конфликт noEmit и declaration

Нельзя одновременно использовать noEmit: true и declaration: true. Если вам нужны declaration файлы, уберите noEmit:

// ❌ Конфликт:
{
  "noEmit": true,      // Не генерировать файлы
  "declaration": true  // Но генерировать declaration файлы
}

// ✅ Правильно для библиотеки:
{
  "noEmit": false,     // Генерировать файлы
  "declaration": true  // Включая declaration файлы
}

// ✅ Правильно для веб-приложения:
{
  "noEmit": true,      // Не генерировать файлы (собирает Vite/Webpack)
  "declaration": false // Declaration не нужны
}

Ошибка 5: Неправильные пути в paths

При использовании paths необходимо также указать baseUrl, иначе алиасы не будут работать:

// ❌ Без baseUrl paths не работает:
{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

// ✅ С baseUrl:
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

Ошибка 6: Компиляция node_modules

Забыть исключить node_modules из компиляции приводит к попытке компиляции сторонних библиотек:

// ❌ Компилятор попытается скомпилировать тысячи файлов из node_modules
// Это очень медленно и вызовет кучу ошибок

// ✅ Всегда исключайте node_modules:
{
  "exclude": ["node_modules"]
}

// Или используйте include вместо exclude:
{
  "include": ["src/**/*.ts"]
}
Чек-лист перед коммитом tsconfig.json:
  • Включён ли strict mode?
  • Правильно ли выбран module (ESNext, commonjs, Node16)?
  • Указаны ли outDir и rootDir?
  • Исключены ли node_modules и dist?
  • Есть ли paths — указан ли baseUrl?
  • Совместимы ли noEmit и declaration?
  • Соответствует ли target среде выполнения?

8. Итоги урока

Что мы узнали:

  • tsconfig.json — это файл конфигурации, который определяет поведение компилятора TypeScript и влияет на работу IDE
  • Создать tsconfig.json можно командой tsc --init, вручную или через VS Code
  • Основные опции: target (версия JS), module (система модулей), strict (строгость проверок), outDir/ rootDir (структура вывода)
  • include и exclude позволяют контролировать набор компилируемых файлов
  • lib определяет доступные встроенные типы (DOM, ES-модули)
  • noEmit отключает генерацию файлов (для проектов с Webpack/Vite)
  • declaration генерирует .d.ts файлы для библиотек
  • sourceMap создаёт карты соответствия для отладки
  • Полезные дополнения: noUnusedLocals, noImplicitReturns, paths, incremental
  • Для монорепозиториев используется механизм extends для наследования конфигураций
  • Типичные ошибки: отсутствие strict, неправильный module, забытый outDir, конфликт noEmit/declaration

Практическое задание: Создайте новый TypeScript-проект, сгенерируйте tsconfig.json командой tsc --init, затем настройте его под свой тип проекта (библиотека, веб-приложение или Node.js сервер). Попробуйте разные опции и посмотрите, как они влияют на поведение компилятора.

Продолжаем изучение TypeScript!

Следующий урок: Работа с типами →

Проверяем понимание

6 вопросов