Параметры compilerOptions, влияющие на Vite

Vite не выполняет полноценную проверку типов TypeScript во время разработки. Основная задача Vite — максимально быстро преобразовывать модули и обеспечивать мгновенную перезагрузку через HMR. Для транспиляции TypeScript Vite использует esbuild, а не компилятор TypeScript (tsc).

Из-за этого параметры compilerOptions в tsconfig.json делятся на несколько категорий:

  • параметры, влияющие на структуру модулей и импортов;
  • параметры, влияющие на поведение IDE;
  • параметры, критически важные для корректной работы Vite;
  • параметры, которые Vite фактически игнорирует во время сборки и разработки.

Пример базовой структуры:

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

Параметр module

Одним из важнейших параметров для Vite является module.

Типичная конфигурация:

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

Почему ESNext важен для Vite

Vite построен вокруг ESM-модулей (ECMAScript Modules). Dev Server работает через нативные ES-модули браузера.

Если указать:

{
  "module": "CommonJS"
}

появятся проблемы:

  • некорректные импорты;
  • конфликты с ESM;
  • ошибки при использовании import.meta;
  • несовместимость с Rollup;
  • нарушения tree-shaking.

Рекомендуемые значения

Наиболее безопасные варианты:

{
  "module": "ESNext"
}

или:

{
  "module": "NodeNext"
}

Однако для большинства Vite-проектов используется именно ESNext.


Параметр target

target определяет, в какой стандарт JavaScript компилируется TypeScript.

Пример:

{
  "compilerOptions": {
    "target": "ES2020"
  }
}

Влияние на Vite

Хотя Vite использует esbuild, параметр target остаётся важным:

  • IDE использует его для проверки кода;
  • TypeScript корректно типизирует API;
  • часть трансформаций зависит от target;
  • Vite учитывает target при работе с современным синтаксисом.

Популярные значения

ES2015

{
  "target": "ES2015"
}

Подходит для старых браузеров, но ограничивает современные возможности JavaScript.

ES2020

{
  "target": "ES2020"
}

Наиболее популярный вариант для современных проектов.

ESNext

{
  "target": "ESNext"
}

Позволяет использовать максимально современный синтаксис.


moduleResolution

Этот параметр особенно важен для Vite 5+.

Современная рекомендация:

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

Режим Bundler

Bundler появился в новых версиях TypeScript специально для современных сборщиков:

  • Vite;
  • Rollup;
  • Webpack;
  • esbuild;
  • Parcel.

Особенности Bundler

Он:

  • правильно понимает ESM;
  • поддерживает package exports;
  • корректно работает с alias;
  • учитывает современные правила резолвинга.

Пример:

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

Сравнение Bundler и Node

Node

{
  "moduleResolution": "Node"
}

Старый алгоритм Node.js.

Проблемы:

  • хуже работает с ESM;
  • не учитывает современные exports;
  • возможны конфликты типов.

Bundler

{
  "moduleResolution": "Bundler"
}

Лучший выбор для Vite-проектов.


allowImportingTsExtensions

Позволяет импортировать .ts-файлы напрямую.

Пример:

{
  "compilerOptions": {
    "allowImportingTsExtensions": true
  }
}

Тогда становится допустимо:

import { sum } from './math.ts'

Особенности

Без этого параметра TypeScript выдаст ошибку.

Однако в большинстве Vite-проектов расширения TypeScript в импортах не используются.

Обычно применяется:

import { sum } from './math'

jsx

Для React-проектов параметр jsx имеет критическое значение.

Пример:

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

Основные варианты jsx

react-jsx

Современный React JSX Transform.

{
  "jsx": "react-jsx"
}

Не требует:

import React from 'react'

react-jsxdev

Используется для development-сборок.

preserve

{
  "jsx": "preserve"
}

JSX сохраняется без преобразования.

Иногда применяется в сложных конфигурациях сборки.


jsxImportSource

Используется при работе с альтернативными JSX-рантаймами.

Например, с Preact:

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "preact"
  }
}

strict

Один из важнейших параметров TypeScript.

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

Что включает strict

strict активирует:

  • strictNullChecks;
  • noImplicitAny;
  • strictFunctionTypes;
  • strictBindCallApply;
  • strictPropertyInitialization;
  • noImplicitThis;
  • alwaysStrict;
  • useUnknownInCatchVariables.

Влияние на Vite

Напрямую на Vite не влияет, но критически важен для качества проекта:

  • предотвращает ошибки;
  • улучшает автодополнение;
  • повышает надёжность рефакторинга;
  • снижает вероятность runtime-багов.

isolatedModules

Очень важный параметр именно для Vite.

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

Почему isolatedModules важен

Vite использует esbuild, который компилирует файлы независимо друг от друга.

isolatedModules заставляет TypeScript проверять совместимость кода с таким режимом.

Что запрещается

Например:

const enum Status {
  Active,
  Disabled
}

или:

namespace App {
}

могут вызывать проблемы.

Практический смысл

Параметр помогает избежать ситуаций, когда:

  • TypeScript компилирует код;
  • а esbuild/Vite не способен обработать его корректно.

useDefineForClassFields

Особенно важен для современных JavaScript-стандартов.

Пример:

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

Поведение class fields

Старое поведение:

class User {
  name = 'Alex'
}

компилировалось через присваивание в конструкторе.

Новое поведение использует стандарт ECMAScript.

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

Некоторые библиотеки:

  • MobX;
  • decorators;
  • ORM;
  • системы реактивности;

могут вести себя по-разному в зависимости от этого параметра.


lib

Определяет набор встроенных API JavaScript и браузера.

Пример:

{
  "compilerOptions": {
    "lib": ["ES2020", "DOM", "DOM.Iterable"]
  }
}

Влияние lib на Vite

Без DOM появятся ошибки:

document.querySelector()
window.addEventListener()

Без современных ES-библиотек будут отсутствовать типы:

Promise
Map
Set
WeakMap

types

Позволяет подключать глобальные типы.

Пример:

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

Зачем нужен vite/client

Без него TypeScript не понимает:

import.meta.env

и:

import.meta.hot

Типичная ошибка

Property 'env' does not exist on type 'ImportMeta'

Решение:

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

baseUrl

Определяет базовую директорию для импортов.

Пример:

{
  "compilerOptions": {
    "baseUrl": "."
  }
}

paths

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

Пример:

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

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

Импорт:

import Button from '@/components/Button'

вместо:

import Button from '../. ./. ./components/Button'

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

TypeScript понимает alias только на уровне типов.

Vite тоже должен знать alias.

Поэтому требуется синхронизация:

import { defineConfig } from 'vite'
import path from 'path'

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

noEmit

Очень распространённый параметр в Vite-проектах.

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

Почему noEmit используется почти всегда

Vite сам занимается сборкой.

TypeScript нужен только для:

  • проверки типов;
  • анализа кода;
  • работы IDE.

Поэтому генерация JS-файлов через tsc обычно не требуется.


skipLibCheck

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

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

Что делает параметр

Отключает проверку .d.ts файлов библиотек.

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

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

Недостатки

Ошибки внутри типов зависимостей могут остаться незамеченными.


resolveJsonModule

Позволяет импортировать JSON как модуль.

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

Пример:

import config from './config.json'

esModuleInterop

Улучшает совместимость CommonJS и ES Modules.

Пример:

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

Типичная проблема без esModuleInterop

Импорт:

import express from 'express'

может работать некорректно.

Приходится писать:

import * as express from 'express'

allowSyntheticDefaultImports

Похожий параметр:

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

Разрешает default import даже там, где его формально нет.

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

{
  "esModuleInterop": true
}

incremental

Включает incremental compilation.

{
  "compilerOptions": {
    "incremental": true
  }
}

TypeScript создаёт служебный файл:

.tsbuildinfo

Польза

  • ускорение повторных проверок;
  • улучшение производительности больших проектов;
  • сокращение времени работы tsc --noEmit.

composite

Используется в monorepo и project references.

{
  "compilerOptions": {
    "composite": true
  }
}

Возможности

Позволяет:

  • связывать несколько tsconfig;
  • строить крупные TypeScript-монорепозитории;
  • ускорять повторные сборки.

declaration

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

{
  "compilerOptions": {
    "declaration": true
  }
}

Когда declaration важен

Особенно актуален для:

  • npm-библиотек;
  • SDK;
  • shared packages;
  • internal libraries.

Для обычного frontend-приложения Vite параметр часто не нужен.


emitDeclarationOnly

Генерация только типов:

{
  "compilerOptions": {
    "emitDeclarationOnly": true
  }
}

Используется при сборке библиотек.


preserveValueImports

Контролирует удаление импортов.

Пример:

{
  "compilerOptions": {
    "preserveValueImports": true
  }
}

Проблематика tree-shaking

Некоторые импорты:

  • используются только как типы;
  • удаляются TypeScript;
  • но нужны bundler-у.

Параметр помогает избежать подобных конфликтов.


verbatimModuleSyntax

Современная альтернатива ряду старых параметров.

{
  "compilerOptions": {
    "verbatimModuleSyntax": true
  }
}

Особенности

TypeScript перестаёт модифицировать импорты и экспорты.

Это особенно хорошо сочетается с:

  • Vite;
  • Rollup;
  • современными ESM-сборщиками.

forceConsistentCasingInFileNames

Критически важен для кроссплатформенной разработки.

{
  "compilerOptions": {
    "forceConsistentCasingInFileNames": true
  }
}

Пример проблемы

Файл:

Button.tsx

Импорт:

import Button from './button'

На Windows это может работать.

На Linux сборка сломается.

Параметр предотвращает подобные ошибки.


allowJs

Разрешает использовать JavaScript вместе с TypeScript.

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

Практическое применение

Полезно при:

  • постепенной миграции на TypeScript;
  • интеграции legacy-кода;
  • смешанных проектах.

checkJs

Включает проверку типов для JavaScript.

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

Теперь TypeScript анализирует:

// @ts-check

и обычные .js файлы.


Типичная конфигурация tsconfig для Vite

Для React + Vite

{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,

    "moduleResolution": "Bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "moduleDetection": "force",
    "noEmit": true,

    "jsx": "react-jsx",

    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,

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

    "types": ["vite/client"]
  },
  "include": ["src"]
}

moduleDetection

Современный параметр TypeScript.

Пример:

{
  "compilerOptions": {
    "moduleDetection": "force"
  }
}

Что делает

TypeScript начинает считать все файлы модулями.

Это снижает вероятность конфликтов глобальной области видимости.

Особенно полезно в крупных Vite-приложениях.


noUnusedLocals и noUnusedParameters

Помогают поддерживать чистоту проекта.

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

Результат

TypeScript обнаруживает:

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

noFallthroughCasesInSwitch

Защищает от ошибок в switch.

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

Пример проблемы

switch (status) {
  case 'loading':
    startLoader()

  case 'success':
    showData()
}

Без break может возникнуть скрытая ошибка.

TypeScript предупредит об этом.


Влияние compilerOptions на производительность Vite

Некоторые параметры напрямую влияют на скорость разработки.

Ускоряющие параметры

{
  "skipLibCheck": true,
  "incremental": true,
  "noEmit": true
}

Потенциально замедляющие

{
  "checkJs": true
}

или чрезмерно строгие проверки в очень крупных monorepo.


Наиболее важные compilerOptions для Vite

Практически обязательными считаются:

{
  "module": "ESNext",
  "moduleResolution": "Bundler",
  "target": "ES2020",
  "isolatedModules": true,
  "noEmit": true,
  "types": ["vite/client"]
}

Для React дополнительно:

{
  "jsx": "react-jsx"
}

Для удобной архитектуры:

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