41. tsconfig.json детально
TypeScript: tsconfig.json детально
Заголовок раздела «TypeScript: tsconfig.json детально»Привет, кодеры! 👋 Яша снова на связи, и сегодня мы погрузимся в сердце каждого серьезного TypeScript-проекта — файл tsconfig.json. Если package.json — это паспорт вашего проекта, то tsconfig.json — это его мозг, конституция и GPS-навигатор в одном флаконе. Он говорит компилятору TypeScript, как именно он должен понимать ваш код, какие правила применять и куда складывать результат.
Для чего он так важен? Возьмем аналогию: вы — опытный шеф-повар, а TypeScript — ваш су-шеф. tsconfig.json — это ваша поваренная книга и список ингредиентов. Без него су-шеф не знает, что готовить, какую температуру использовать и куда подавать блюдо. В продвинутых проектах, особенно с монорепозиториями или сложными структурами, правильно настроенный tsconfig.json критически важен для производительности, корректности типов и поддержания порядка.
🧠 Анатомия tsconfig.json: compilerOptions
Заголовок раздела «🧠 Анатомия tsconfig.json: compilerOptions»Самая большая и важная секция в tsconfig.json — это compilerOptions. Здесь мы указываем компилятору все его инструкции.
// tsconfig.json (пример базовой конфигурации){ "compilerOptions": { // 1. target: Какую версию ECMAScript мы хотим получить на выходе // Современные приложения часто используют ES2020 или ESNext "target": "ES2022",
// 2. module: Какую систему модулей использовать для сгенерированного JS // CommonJS для Node.js, ESNext для бандлеров типа Webpack/Vite "module": "ESNext",
// 3. lib: Какие стандартные библиотеки доступны в глобальной области видимости // Включает API браузера, DOM, ES-функции. // 'DOM' для браузерных проектов, 'ESNext' для всех новейших фич. "lib": ["ES2022", "DOM", "DOM.Iterable"],
// 4. outDir: Куда компилировать выходные JS-файлы "outDir": "./dist",
// 5. rootDir: Где искать исходные TS-файлы "rootDir": "./src",
// 6. strict: Включить все "строгие" проверки типов // Настоятельно рекомендуется для всех проектов! Он включает strictNullChecks, // noImplicitAny, strictFunctionTypes и другие. "strict": true,
// 7. esModuleInterop: Позволяет использовать синтаксис импорта CommonJS-модулей // как ES-модулей (import * as React from 'react' -> import React from 'react') "esModuleInterop": true,
// 8. skipLibCheck: Пропускать проверку типов файлов деклараций из node_modules // Ускоряет компиляцию, но может скрыть проблемы в библиотеках. "skipLibCheck": true,
// 9. forceConsistentCasingInFileNames: Гарантирует, что имена файлов // всегда используются с согласованным регистром (для предотвращения проблем на разных ОС). "forceConsistentCasingInFileNames": true,
// 10. declaration: Генерировать .d.ts файлы деклараций типов // Необходимо при создании библиотек, чтобы потребители могли получить типы. "declaration": true, // 11. declarationMap: Генерировать sourcemap для .d.ts файлов // Полезно для отладки типов в сторонних инструментах. "declarationMap": true,
// 12. jsx: Как компилятор должен обрабатывать JSX // "react-jsx" для нового JSX-трансформатора React. "jsx": "react-jsx",
// 13. moduleResolution: Как компилятор разрешает модули. // "Node" для Node.js, "NodeNext" для более строгих правил Node.js. "moduleResolution": "Node",
// 14. baseUrl: Базовый путь для разрешения не относительных модулей // Используется с "paths" для создания алиасов. "baseUrl": "./src", // 15. paths: Карта для разрешения модулей. Идеально для абсолютных импортов. "paths": { "@utils/*": ["./utils/*"], "@components/*": ["./components/*"] } }, // 16. include: Массив globs (шаблонов), которые указывают, какие файлы // должны быть включены в проект для компиляции. "include": ["src/**/*.ts", "src/**/*.tsx"], // 17. exclude: Массив globs, которые следует исключить // (даже если они попали под "include"). "exclude": ["node_modules", "dist"]}🛣️ Модули и Алиасы: baseUrl и paths
Заголовок раздела «🛣️ Модули и Алиасы: baseUrl и paths»Для больших проектов или монорепозиториев абсолютные импорты — это спасение. baseUrl и paths позволяют нам настроить собственные алиасы, делая код чище и удобнее для рефакторинга.
Представьте, что у вас есть такая структура:
src/├── components/│ └── Button.tsx├── utils/│ └── helpers.ts└── index.tsБез paths вам пришлось бы писать:
import { Button } from './components/Button';import { sum } from './utils/helpers';С baseUrl и paths (как в примере выше):
import { Button } from '@components/Button'; // Красиво, не правда ли?import { sum } from '@utils/helpers';Но помните: если вы используете paths, вам также нужно настроить ваш бандлер (Webpack, Vite, Rollup) или среду выполнения (Node.js с module-alias или tsconfig-paths), чтобы он тоже понимал эти алиасы. TypeScript видит их, но рантайм по умолчанию — нет!
🌲 Расширение конфигураций: extends
Заголовок раздела «🌲 Расширение конфигураций: extends»Один из самых мощных инструментов для управления tsconfig.json в монорепозиториях или крупных проектах — это extends. Он позволяет одной конфигурации наследовать поля из другой, а затем переопределять или дополнять их.
Предположим, у вас есть общая “базовая” конфигурация для всех проектов:
{ "compilerOptions": { "target": "ES2022", "module": "ESNext", "lib": ["ES2022", "DOM", "DOM.Iterable"], "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "declaration": true, "declarationMap": true, "moduleResolution": "Node", "baseUrl": "./src", "paths": { "@utils/*": ["./utils/*"] } }}А теперь у вас есть два проекта: frontend (с React) и backend (Node.js).
{ "extends": "../../base-tsconfig.json", // Путь к базовому файлу "compilerOptions": { "jsx": "react-jsx", // Добавляем специфичную для React настройку "outDir": "./dist" }, "include": ["src/**/*.ts", "src/**/*.tsx"]}{ "extends": "../../base-tsconfig.json", "compilerOptions": { "lib": ["ES2022"], // Переопределяем lib, DOM не нужен для бэкенда "outDir": "./build" }, "include": ["src/**/*.ts"]}Файлы base-tsconfig.json обычно не включают include/exclude, чтобы каждый проект мог определять свои собственные границы.
🔗 Ссылки на проекты: references
Заголовок раздела «🔗 Ссылки на проекты: references»Для монорепозиториев references — это убийственная фича! Она позволяет TypeScript понять взаимозависимости между подпроектами, что дает несколько ключевых преимуществ:
- Быстрая инкрементальная сборка: TypeScript компилирует только те проекты и их зависимости, которые изменились.
- Улучшенная навигация и автодополнение: IDE лучше понимает связи между проектами.
- Типобезопасность на границах проектов: Гарантирует, что изменения в одной библиотеке корректно отражаются в ее потребителях.
Пример структуры монорепо:
my-monorepo/├── packages/│ ├── ui-kit/│ │ ├── src/│ │ │ └── Button.ts│ │ └── tsconfig.json│ └── utils/│ ├── src/│ │ └── math.ts│ └── tsconfig.json└── apps/ └── web/ ├── src/ │ └── index.ts └── tsconfig.json{ "compilerOptions": { "composite": true, // Обязательно для всех референсных проектов "outDir": "./dist", "declaration": true, "declarationMap": true, // ... другие compilerOptions }, "include": ["src"], "exclude": ["node_modules"]}{ "compilerOptions": { "composite": true, "outDir": "./dist", "declaration": true, "declarationMap": true, // ... другие compilerOptions }, "include": ["src"], "exclude": ["node_modules"], "references": [ // Этот проект зависит от @my-monorepo/utils { "path": "../utils" } ]}{ "compilerOptions": { "outDir": "./dist", // ... другие compilerOptions, например, для React }, "include": ["src"], "exclude": ["node_modules"], "references": [ // Веб-приложение зависит от UI-kit и Utils { "path": "../../packages/ui-kit" }, { "path": "../../packages/utils" } ]}Теперь, когда вы запускаете tsc --build (или просто tsc -b) в корне, TypeScript умно соберет все проекты в правильном порядке, пересобирая только то, что нужно.
🚫 Типичные ошибки и их решения
Заголовок раздела «🚫 Типичные ошибки и их решения»-
“Cannot find module ’…’ or its corresponding type declarations.”
- Причина: Чаще всего это проблема с
paths,baseUrlили отсутствующимиtypes/typeRootsвcompilerOptions. Также может быть, что библиотека не имеет файлов деклараций (.d.ts), и вам нужно установить@types/название-пакета. - Решение: Проверьте
pathsиbaseUrl. Убедитесь, что ваш бандлер также настроен для разрешения этих алиасов. Установите необходимые@typesпакеты.
- Причина: Чаще всего это проблема с
-
“Property ‘x’ does not exist on type ’…’.”
- Причина: Очень часто это из-за включенного
strict: true(илиstrictNullChecks: true), когда вы пытаетесь получить доступ к потенциальноnullилиundefinedзначению без проверки. Также может быть, что вlibне включены нужные библиотеки (например,DOMдля браузерных API). - Решение: Используйте операторы
?.(optional chaining) и??(nullish coalescing), делайте проверки наnull/undefined. ПроверьтеlibвcompilerOptions.
- Причина: Очень часто это из-за включенного
-
“Duplicate identifier ’…’.”
- Причина: Ваши
includeилиexcludeнастроены таким образом, что TypeScript пытается скомпилировать один и тот же файл дважды, или вы включаете скомпилированные.jsфайлы вместе с их.tsисходниками. - Решение: Уточните
includeиexclude. Убедитесь, чтоoutDirнаходится вexclude.
- Причина: Ваши
-
Проблемы с JSX/React.
- Причина: Неправильно настроен
jsxвcompilerOptions, илиlibне включаетDOM. - Решение: Установите
jsxвreact-jsx(для React 17+) илиreact(для старых версий). Убедитесь, чтоlibсодержитDOM.
- Причина: Неправильно настроен
🎯 Практика
Заголовок раздела «🎯 Практика»-
Настройка монорепо с
extendsиreferences:- Создайте корневой
base-tsconfig.jsonс общимиcompilerOptions. - Создайте две папки:
packages/shared-uiиapps/web. - В
packages/shared-uiсоздайтеtsconfig.json, которыйextendsкорневой, добавляетcomposite: trueиdeclaration: true. Создайте простую функцию/компонент. - В
apps/webсоздайтеtsconfig.json, которыйextendsкорневой, добавляетjsx: "react-jsx"иreferencesнаshared-ui. Импортируйте компонент изshared-uiи используйте его. - Попробуйте скомпилировать все с помощью
tsc -b.
- Создайте корневой
-
Алиасы с
paths:- В вашем проекте (или в созданном на шаге 1) настройте
baseUrlиpathsтак, чтобы можно было импортировать файлы изsrc/utilsкак@utils/*и изsrc/servicesкак@services/*. - Добавьте соответствующие импорты в одном из ваших
.tsфайлов и убедитесь, что TypeScript их понимает. - (Бонусное задание): Если вы используете Webpack/Vite, настройте их, чтобы они также понимали эти алиасы.
- В вашем проекте (или в созданном на шаге 1) настройте
-
Эксперименты со
strictиlib:- Возьмите небольшой
.tsфайл с несколькими переменными, которые могут бытьnull/undefinedи без явных проверок. - Попробуйте скомпилировать его с
strict: false, затем поменяйте наstrict: true. Какие ошибки появились? Как их исправить? - Удалите
DOMизlibвcompilerOptionsи попробуйте использоватьdocument.getElementById(). Какие ошибки выдаст компилятор?
- Возьмите небольшой
💡 Совет
Заголовок раздела «💡 Совет»Всегда начинайте с strict: true! Это лучшая инвестиция в стабильность и поддерживаемость вашего кода. Поначалу будет больно, но это избавит вас от бесчисленных багов в будущем. Используйте extends для создания единой, хорошо поддерживаемой базовой конфигурации, а references — для оптимизации сборок в монорепозиториях. Ваш TypeScript-компилятор — ваш лучший друг, и tsconfig.json — это язык, на котором вы с ним общаетесь. Освойте его в совершенстве!