tsconfig.json и совместимость с Vite

Файл tsconfig.json управляет поведением компилятора TypeScript и определяет, как проект анализируется, проверяется и преобразуется. В экосистеме Vite этот файл играет особую роль, поскольку Vite использует TypeScript иначе, чем классические сборщики.

Vite не выполняет полноценную компиляцию TypeScript через tsc во время разработки. Вместо этого применяется быстрый транспайлинг через esbuild, а типизация остаётся задачей самого TypeScript. Из-за этого часть параметров tsconfig.json влияет только на IDE и проверку типов, а часть — непосредственно на работу Vite.


Базовая структура tsconfig.json

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

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "Node",
    "strict": true,
    "jsx": "react-jsx"
  }
}

Каждая настройка оказывает влияние на совместимость проекта с Vite.


Параметр target

Параметр target определяет, в какую версию JavaScript должен преобразовываться TypeScript-код.

Пример:

{
  "compilerOptions": {
    "target": "ES2017"
  }
}

В проектах Vite чаще всего используется:

{
  "compilerOptions": {
    "target": "ESNext"
  }
}

Причины:

  • Vite ориентирован на современные браузеры;
  • esbuild эффективно работает с современным синтаксисом;
  • уменьшается объём транспиляции;
  • сохраняется поддержка современных возможностей JavaScript.

Если используется слишком старый target, могут возникать:

  • лишние преобразования;
  • снижение производительности сборки;
  • несовместимость с ESM;
  • проблемы с динамическими импортами.

Параметр module

Vite построен вокруг ES Modules, поэтому параметр module практически всегда должен быть:

{
  "compilerOptions": {
    "module": "ESNext"
  }
}

Некорректные значения:

{
  "compilerOptions": {
    "module": "CommonJS"
  }
}

Использование CommonJS приводит к проблемам:

  • ломается нативный ESM-подход Vite;
  • возникают ошибки импортов;
  • перестают корректно работать динамические импорты;
  • ухудшается tree shaking.

moduleResolution и совместимость с Vite

Классический вариант

Долгое время использовалось:

{
  "compilerOptions": {
    "moduleResolution": "Node"
  }
}

Этот режим имитирует поведение Node.js при поиске модулей.


Современный режим Bundler

Начиная с TypeScript 5 появился новый вариант:

{
  "compilerOptions": {
    "moduleResolution": "Bundler"
  }
}

Для Vite это наиболее корректный вариант.

Преимущества:

  • правильная работа с ESM;
  • совместимость с exports и imports;
  • корректное разрешение путей;
  • лучшее соответствие реальному поведению Vite.

Рекомендуемая современная конфигурация:

{
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "Bundler"
  }
}

useDefineForClassFields

Vite-шаблоны TypeScript обычно включают:

{
  "compilerOptions": {
    "useDefineForClassFields": true
  }
}

Эта настройка переводит поля классов на современную спецификацию JavaScript.

Без неё возможны:

  • различия между поведением Babel и TypeScript;
  • несовместимость с библиотеками;
  • неожиданные ошибки при наследовании.

Строгая типизация

Практически все современные Vite-проекты используют:

{
  "compilerOptions": {
    "strict": true
  }
}

Этот режим включает:

  • строгую проверку null;
  • строгую проверку функций;
  • контроль типов параметров;
  • анализ потенциально опасного кода.

Дополнительные полезные настройки:

{
  "compilerOptions": {
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
  }
}

isolatedModules

Для Vite крайне важен параметр:

{
  "compilerOptions": {
    "isolatedModules": true
  }
}

Причина заключается в том, что esbuild компилирует файлы независимо друг от друга.

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

Без этой настройки код может:

  • успешно проходить проверку IDE;
  • но ломаться во время сборки Vite.

esModuleInterop

Часто используется:

{
  "compilerOptions": {
    "esModuleInterop": true
  }
}

Настройка улучшает совместимость между:

  • CommonJS;
  • ES Modules;
  • старым npm-кодом.

Особенно полезна при работе с устаревшими библиотеками.


allowSyntheticDefaultImports

Обычно включается вместе с esModuleInterop:

{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true
  }
}

Позволяет писать:

import React from "react";

даже если библиотека не экспортирует default.


Работа JSX в Vite

React

Для React используется:

{
  "compilerOptions": {
    "jsx": "react-jsx"
  }
}

Это включает новый JSX runtime.

Старый вариант:

{
  "compilerOptions": {
    "jsx": "react"
  }
}

требует обязательного импорта React в каждом файле.


Vue

В Vue-проектах JSX часто не нужен вообще:

{
  "compilerOptions": {
    "jsx": "preserve"
  }
}

Solid

Для SolidJS используется:

{
  "compilerOptions": {
    "jsx": "preserve",
    "jsxImportSource": "solid-js"
  }
}

Работа путей через paths

Одна из самых популярных возможностей TypeScript — псевдонимы импортов.

Пример:

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

После этого можно писать:

import Button from "@/components/Button";

вместо:

import Button from "../. ./. ./components/Button";

Важная особенность Vite и paths

TypeScript знает о paths, но сам Vite — нет.

Поэтому требуется дополнительная настройка:

import { defineConfig } from "vite";
import path from "path";

export default defineConfig({
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src")
    }
  }
});

Без этого:

  • IDE не показывает ошибок;
  • TypeScript компилируется;
  • но Vite не находит модуль.

Использование vite-tsconfig-paths

Для автоматической синхронизации путей применяется плагин:

npm install vite-tsconfig-paths

Подключение:

import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";

export default defineConfig({
  plugins: [tsconfigPaths()]
});

Преимущества:

  • единый источник путей;
  • меньше дублирования;
  • упрощение конфигурации;
  • уменьшение количества ошибок.

types и глобальные типы Vite

Vite предоставляет собственные глобальные типы.

Обычно используется:

{
  "compilerOptions": {
    "types": ["vite/client"]
  }
}

Это добавляет поддержку:

import.meta.env

Пример:

console.log(import.meta.env.VITE_API_URL);

Без подключения типов TypeScript выдаёт ошибку.


Типизация переменных окружения

Для строгой типизации создаётся файл:

/// <reference types="vite/client" />

interface ImportMetaEnv {
  readonly VITE_API_URL: string;
  readonly VITE_APP_TITLE: string;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

Теперь TypeScript знает структуру .env.


include и exclude

Пример настройки

{
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}

Почему это важно

Неправильный include может:

  • замедлить TypeScript;
  • увеличить потребление памяти;
  • привести к анализу лишних файлов.

Разделение конфигураций

В крупных проектах часто используется несколько файлов:

tsconfig.json
tsconfig.app.json
tsconfig.node.json

Основной tsconfig.json

Часто содержит только общие настройки:

{
  "files": [],
  "references": [
    { "path": "./tsconfig.app.json" },
    { "path": "./tsconfig.node.json" }
  ]
}

Конфигурация приложения

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "strict": true
  },
  "include": ["src"]
}

Конфигурация Node.js

Для vite.config.ts могут потребоваться другие настройки:

{
  "compilerOptions": {
    "composite": true,
    "module": "ESNext",
    "moduleResolution": "Node"
  },
  "include": ["vite.config.ts"]
}

Project References

TypeScript поддерживает ссылки между проектами:

{
  "references": [
    { "path": "../shared" }
  ]
}

Это особенно полезно:

  • в монорепозиториях;
  • при разделении frontend/backend;
  • в больших корпоративных проектах.

Совместимость с vite.config.ts

Конфигурация Vite сама может быть написана на TypeScript:

import { defineConfig } from "vite";

export default defineConfig({
  server: {
    port: 3000
  }
});

Для корректной типизации требуется:

{
  "compilerOptions": {
    "types": ["node"]
  }
}

skipLibCheck

Очень популярная оптимизация:

{
  "compilerOptions": {
    "skipLibCheck": true
  }
}

Преимущества:

  • ускорение проверки типов;
  • уменьшение нагрузки на IDE;
  • устранение проблем со сторонними типами.

Недостаток:

  • TypeScript перестаёт проверять .d.ts библиотеки.

noEmit

В проектах Vite часто используется:

{
  "compilerOptions": {
    "noEmit": true
  }
}

Причина:

  • Vite выполняет сборку самостоятельно;
  • TypeScript нужен только для типизации.

Если оставить генерацию файлов включённой:

  • могут появляться лишние .js;
  • усложняется структура проекта;
  • возрастает риск конфликтов.

Поддержка JSON

Для импорта JSON-файлов:

{
  "compilerOptions": {
    "resolveJsonModule": true
  }
}

После этого возможно:

import data from "./data.json";

Поддержка JavaScript-файлов

В смешанных проектах:

{
  "compilerOptions": {
    "allowJs": true
  }
}

Иногда дополнительно:

{
  "compilerOptions": {
    "checkJs": true
  }
}

Это включает типизацию обычного JavaScript.


Современная рекомендуемая конфигурация для Vite

{
  "compilerOptions": {
    "target": "ESNext",
    "useDefineForClassFields": true,
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "strict": true,
    "jsx": "react-jsx",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "esModuleInterop": true,
    "noEmit": true,
    "skipLibCheck": true,
    "types": ["vite/client"]
  },
  "include": ["src"]
}

Типичные ошибки совместимости

Использование CommonJS

Ошибка:

{
  "compilerOptions": {
    "module": "CommonJS"
  }
}

Последствия:

  • проблемы с импортами;
  • ошибки ESM;
  • конфликты с Vite plugins.

Отсутствие vite/client

Ошибка:

import.meta.env

TypeScript сообщает:

Property 'env' does not exist

Решение:

{
  "compilerOptions": {
    "types": ["vite/client"]
  }
}

Несовпадение alias

TypeScript:

{
  "paths": {
    "@/*": ["src/*"]
  }
}

Vite:

resolve: {
  alias: {}
}

Результат:

  • IDE работает;
  • Vite выдаёт ошибку Failed to resolve import.

Отключённый isolatedModules

Возможны проблемы:

  • с enum;
  • с type-only импортами;
  • с re-export;
  • с Babel/esbuild совместимостью.

Проверка типов в Vite

Во время разработки Vite не запускает полноценный tsc.

Для отдельной проверки обычно используется:

tsc --noEmit

или:

vue-tsc --noEmit

для Vue-проектов.


Использование vite-plugin-checker

Плагин позволяет видеть ошибки типов прямо в браузере.

Установка:

npm install vite-plugin-checker -D

Подключение:

import checker from "vite-plugin-checker";

export default defineConfig({
  plugins: [
    checker({
      typescript: true
    })
  ]
});

Преимущества:

  • мгновенная диагностика;
  • интеграция с HMR;
  • удобство разработки.

Влияние tsconfig.json на IDE

Редакторы вроде PhpStorm и Visual Studio Code используют tsconfig.json для:

  • автодополнения;
  • навигации;
  • анализа типов;
  • рефакторинга;
  • проверки импортов.

Ошибки конфигурации часто проявляются именно через IDE:

  • ложные ошибки;
  • отсутствие подсказок;
  • неверные пути;
  • некорректная типизация.

Особенности monorepo и Vite

В монорепозиториях важно:

  • синхронизировать paths;
  • использовать Project References;
  • избегать конфликтов node_modules;
  • согласовывать ESM-настройки.

Часто применяется базовый файл:

{
  "compilerOptions": {
    "strict": true,
    "module": "ESNext",
    "moduleResolution": "Bundler"
  }
}

который затем наследуется:

{
  "extends": "../. ./tsconfig.base.json"
}

Наследование конфигураций

TypeScript поддерживает extends:

{
  "extends": "./tsconfig.base.json",
  "compilerOptions": {
    "strict": false
  }
}

Это позволяет:

  • уменьшить дублирование;
  • стандартизировать настройки;
  • упростить поддержку больших проектов.