Vite, структура проекта, строгий tsconfig, ESLint, React Compiler, зависимости, окружение и воспроизводимая сборка.
Инструментарий должен быстро сообщать об ошибке и одинаково работать на ноутбуке, в CI и в рабочей сборке.
Вы соберёте минимальный проект без Create React App, разделите проверку типов и сборку, включите правила React и получите воспроизводимый набор проверок качества.
Понадобятся основы ES-модулей, package.json и командной строки. Итог урока — проект, в котором одной командой запускаются проверка типов, линтер, модульные тесты и рабочая сборка.
Для изучения клиентского React удобно создать Vite-проект по шаблону react-ts. Для нового рабочего приложения React рекомендует сначала оценить подходящий фреймворк: маршрутизация, загрузка данных и способ отрисовки уже являются частью архитектуры. Учебный стенд на Vite позволяет увидеть сам React без скрытого серверного слоя.
npm create vite@9.1.1 react-lab -- --template react-ts
cd react-lab
npm install
npm run devВерсия create-vite в команде зафиксирована для воспроизводимости самого
каркаса. Шаблон не спрашивает, какой линтер использовать, и его текущие
зависимости не являются версионным контрактом курса. Для курса сохраните
TypeScript 5.9 и Vite 8.1 в package.json, установите ESLint явно и зафиксируйте
получившийся граф в package-lock.json:
npm install --save-dev --save-exact typescript@5.9.3 vite@8.1.5
npm install --save-dev eslint @eslint/js typescript-eslint eslint-plugin-react-hooksПеред обновлением create-vite или зависимостей повторите все проверки из
раздела 8. Лабораторный стенд уже содержит согласованный файл блокировки, поэтому
для выполнения задания его нужно устанавливать через npm ci, а не создавать
заново этой командой.
Команда создаёт ESM-проект с преобразованием JSX и TypeScript, но не выбирает за вас маршрутизацию, кеш данных и SSR.
Проверьте себя. Запишите в README версии Node и диспетчера пакетов, а также команды запуска и рабочей сборки.
Частая ошибка. Начать новый проект с устаревшего Create React App или спрятать все механизмы внутри большого фреймворка до изучения модели отрисовки.
Сборщик может удалить типы и получить JavaScript даже при ошибках модели. Поэтому tsc --noEmit запускается отдельно. Для React-проекта важны strict, noUncheckedIndexedAccess, exactOptionalPropertyTypes, useUnknownInCatchVariables, современное преобразование JSX и совместимое со сборщиком разрешение модулей. Каждый дополнительный флаг включают вместе с исправлением кода, а не с массовым подавлением ошибок.
Отредактируйте tsconfig.app.json — именно в этом файле хранятся настройки TypeScript для клиентского кода:
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"useUnknownInCatchVariables": true,
"jsx": "react-jsx",
"moduleResolution": "bundler",
"noEmit": true
}
}Что значит каждый флаг:
strict — включает весь набор строгих проверок: запрет неявного any, проверку this, строгие функции и т. д.noUncheckedIndexedAccess — доступ по индексу массива или ключу объекта возвращает T | undefined, а не просто T. Это заставляет проверять наличие элемента перед использованием.exactOptionalPropertyTypes — свойство с ? нельзя передать как undefined, только опустить совсем. Разница между «свойства нет» и «свойство равно undefined».useUnknownInCatchVariables — переменная в catch (e) имеет тип unknown вместо any. Приходится проверять тип ошибки перед чтением свойств.jsx: "react-jsx" — автоматическое преобразование JSX без обязательного import React.moduleResolution: "bundler" — разрешение модулей, совместимое с Vite и другими сборщиками.noEmit — TypeScript проверяет типы, но не создаёт JavaScript-файлы. Сборку делает Vite.Проверьте себя. Добавьте намеренно небезопасный доступ по индексу и убедитесь, что CI его отклоняет.
Частая ошибка. Считать наличие .tsx доказательством строгой типизации.
Клиентская сборка подставляет разрешённые переменные окружения в JavaScript. Всё, что попало в браузерный пакет, доступно пользователю независимо от имени. В Vite клиент по умолчанию видит только переменные с префиксом VITE_; это фильтр экспорта, а не хранилище секретов. import.meta.env нужно типизировать, а значения — проверять при запуске.
Создайте файл src/vite-env.d.ts (Vite уже создал его при инициализации) и добавьте описание интерфейса:
interface ImportMetaEnv {
readonly VITE_API_BASE_URL: string;
}Теперь в любом компоненте import.meta.env.VITE_API_BASE_URL будет иметь тип string, а обращение к несуществующей переменной — ошибку компиляции:
const apiBase = new URL(import.meta.env.VITE_API_BASE_URL);Аннотация помогает TypeScript, но пустую или неверную строку всё равно нужно проверить во время выполнения, например через URL или схему.
Проверьте себя. Сломайте URL в .env и добейтесь ранней понятной ошибки вместо запросов на undefined/path.
Частая ошибка. Положить секрет API в VITE_API_SECRET, потому что в имени есть слово SECRET. Префикс VITE_ не скрывает значение — оно попадёт в браузерный пакет и будет видно любому пользователю через DevTools. Секреты хранят только на сервере.
Компилятор типов не считает ошибкой нарушение порядка хуков, небезопасное чтение ref во время отрисовки или цикл обновлений состояния. Эти ограничения проверяет eslint-plugin-react-hooks. Актуальные правила входят в рекомендуемую конфигурацию и полезны даже до включения React Compiler.
// eslint.config.js
import { defineConfig } from 'eslint/config';
import reactHooks from 'eslint-plugin-react-hooks';
export default defineConfig([
reactHooks.configs.flat.recommended,
]);Правила проверяют семантические ограничения React. Отключая предупреждение, объясните, почему код остаётся корректным, а не просто добивайтесь зелёного CI.
Проверьте себя. Создайте условный вызов хука и обновление состояния во время отрисовки, затем сравните сообщения.
Частая ошибка. Оставить только форматтер и ожидать, что он найдёт нарушение правил хуков.
React Compiler 1.0 стабилен и автоматически мемоизирует компоненты и хуки на основе анализа данных и мутаций. Новый код следует писать чисто и декларативно, позволяя компилятору выбирать границы. useMemo, useCallback и memo остаются для измеренных узких случаев и семантически необходимой стабильной ссылки.
Проверьте себя. Включите React Compiler для одной области, проверьте правила линтера и сравните тесты до и после.
Частая ошибка. Механически удалить всю ручную мемоизацию из старого проекта сразу после установки Compiler.
package.json задаёт допустимые диапазоны, а файл блокировки фиксирует точный граф зависимостей и контрольные суммы. В CI используйте чистую установку (npm ci для npm), которая не изменяет этот файл. Версии Node и диспетчера пакетов также фиксируются. Обновление зависимостей оформляется отдельным изменением с тестами.
Проверьте себя. Удалите node_modules, выполните чистую установку по файлу блокировки и сравните дерево зависимостей в двух запусках.
Частая ошибка. Игнорировать файл блокировки приложения и получать разные косвенные зависимости в CI.
ES-модули формируют статический граф, по которому сборщик удаляет неиспользуемый код и создаёт части пакета. Динамический import() задаёт асинхронную границу, если путь можно проанализировать. Разбиение оправдано маршрутом или редко используемой тяжёлой возможностью, а не желанием получить больше файлов. Измеряйте загруженные байты и задержку взаимодействия.
// src/Reports.tsx
import { lazy, Suspense } from 'react';
const ReportEditor = lazy(() => import('./ReportEditor.tsx'));
export function Reports() {
return <Suspense fallback={<p>Загрузка редактора…</p>}><ReportEditor /></Suspense>;
}lazy ожидает экспорт модуля по умолчанию, а Suspense задаёт видимое состояние загрузки части пакета. Ошибку загрузки обрабатывает граница ошибок.
Проверьте себя. Сравните начальный пакет и последовательность сетевых запросов до и после выделения редко открываемого редактора.
Частая ошибка. Лениво загружать маленькую кнопку, создавая дополнительный сетевой запрос на основном пути.
Один скрипт верхнего уровня должен объединять проверки типов, линтер, модульные и интеграционные тесты и сборку. Сквозные тесты можно оставить отдельным более дорогим этапом. Сообщение должно указывать слой ошибки: типы, правило React, поведение или сборка. Кеш CI ускоряет повтор, но его ключ должен учитывать файл блокировки и конфигурацию.
// package.json
{
"scripts": {
"typecheck": "tsc --noEmit",
"lint": "eslint . --max-warnings=0",
"test": "vitest run",
"build": "vite build",
"check": "npm run typecheck && npm run lint && npm run test && npm run build"
}
}Сначала запускаются быстрые диагностические проверки, затем более дорогая рабочая сборка.
Проверьте себя. Внесите по одной ошибке каждого класса и убедитесь, что сообщение ведёт к первопричине.
Частая ошибка. Подавить код завершения команд, чтобы конвейер всегда был зелёным.
Настройте приложение каталога по шаблону Vite react-ts: включите строгие флаги TypeScript, плоскую конфигурацию ESLint с правилами хуков и React Compiler, типизированные переменные окружения и команду CI npm run check.
Работа готова, когда чистая копия проекта устанавливается по файлу блокировки, а ошибка типа, правила хуков или теста останавливает процесс до сборки.
Далее: JSX и типизированные компоненты