← Курс/tsconfig.json — конфигурация TypeScript#185 из 257+25 XP

tsconfig.json — конфигурация TypeScript

Основная структура

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "strict": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

strict — главная опция

strict: true включает целый пакет проверок одной строкой:

{
  "compilerOptions": {
    "strict": true
    // Эквивалентно включению всех этих опций:
    // "strictNullChecks": true       — null/undefined не присваиваются другим типам
    // "noImplicitAny": true          — нельзя иметь неявный any
    // "strictFunctionTypes": true    — строгая проверка типов функций
    // "strictBindCallApply": true    — проверка bind/call/apply
    // "strictPropertyInitialization": true — свойства класса должны быть инициализированы
    // "noImplicitThis": true         — this должен иметь явный тип
    // "alwaysStrict": true           — добавляет "use strict" в каждый файл
  }
}

target — куда компилировать

Определяет какой JavaScript на выходе:

{
  "compilerOptions": {
    "target": "ES5"      // IE совместимость — стрелки → function, let → var
    "target": "ES2015"   // async/await, классы, стрелки
    "target": "ES2020"   // optional chaining, nullish coalescing
    "target": "ES2022"   // top-level await, private fields (#)
    "target": "ESNext"   // самые новые возможности
  }
}

module и moduleResolution

{
  "compilerOptions": {
    // Формат модулей в скомпилированном коде:
    "module": "CommonJS",  // require/module.exports — Node.js
    "module": "ESNext",    // import/export — браузеры и современный Node
    "module": "Node16",    // для Node.js 16+ с .mjs/.cjs

    // Как TypeScript ищет модули:
    "moduleResolution": "bundler",  // для Vite/webpack (современный)
    "moduleResolution": "node16",   // для Node.js 16+
    "moduleResolution": "node"      // классический Node.js алгоритм
  }
}

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

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@components/*": ["src/components/*"],
      "@utils/*": ["src/utils/*"]
    }
  }
}
// Теперь вместо:
import { Button } from '../../../components/Button'
// Можно:
import { Button } from '@components/Button'

Рекомендуемые конфигурации

Для Node.js:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "strict": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "esModuleInterop": true
  }
}

Для React (Vite):

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "jsx": "react-jsx",
    "strict": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"]
  }
}

Для монорепо (`composite`):

{
  "compilerOptions": {
    "composite": true,     // Включает инкрементальную сборку
    "declaration": true,   // Генерирует .d.ts файлы
    "declarationMap": true // Source maps для .d.ts
  }
}

Другие важные опции

{
  "compilerOptions": {
    "lib": ["ES2020", "DOM"],  // Какие встроенные типы доступны
    "jsx": "react-jsx",        // Как обрабатывать JSX
    "sourceMap": true,         // Генерировать source maps
    "declaration": true,       // Генерировать .d.ts файлы
    "noUnusedLocals": true,    // Ошибка на неиспользуемые переменные
    "noUnusedParameters": true, // Ошибка на неиспользуемые параметры
    "exactOptionalPropertyTypes": true  // Точные опциональные типы
  }
}

Примеры

Симуляция tsconfig: объект конфигурации с профилями, validateConfig и mergeConfigs — паттерны конфигурирования в JS

// tsconfig.json — это просто JSON-конфиг для TypeScript компилятора.
// В JS симулируем те же концепции: профили конфигурации,
// валидацию и слияние настроек.

// Базовые профили конфигурации (аналоги рекомендуемых tsconfig)
const CONFIG_PROFILES = {
  base: {
    strict: true,
    sourceMap: true,
    declaration: true,
    noUnusedLocals: true,
    noUnusedParameters: true,
  },

  node: {
    target: 'ES2022',
    module: 'CommonJS',
    moduleResolution: 'node',
    outDir: './dist',
    rootDir: './src',
    esModuleInterop: true,
  },

  react: {
    target: 'ES2020',
    module: 'ESNext',
    moduleResolution: 'bundler',
    jsx: 'react-jsx',
    lib: ['ES2020', 'DOM', 'DOM.Iterable'],
  },

  nextjs: {
    target: 'ES2017',
    module: 'ESNext',
    moduleResolution: 'bundler',
    jsx: 'preserve',
    lib: ['DOM', 'DOM.Iterable', 'ESNext'],
    allowJs: true,
    skipLibCheck: true,
    forceConsistentCasingInFileNames: true,
  }
}

// Функция слияния конфигов (base перекрывается override)
function mergeConfigs(...configs) {
  return configs.reduce((acc, config) => {
    const result = Object.assign({}, acc)
    Object.keys(config).forEach(key => {
      if (Array.isArray(config[key]) && Array.isArray(acc[key])) {
        // Массивы (например lib) объединяются без дублей
        result[key] = [...new Set([...acc[key], ...config[key]])]
      } else {
        result[key] = config[key]
      }
    })
    return result
  }, {})
}

// Валидация совместимости настроек
function validateConfig(config) {
  const errors = []
  const warnings = []

  // Проверка совместимости target и module
  if (config.module === 'CommonJS' && config.target === 'ESNext') {
    warnings.push('module: CommonJS с target: ESNext — нестандартная комбинация')
  }

  // Рекомендуем strict для production
  if (!config.strict) {
    warnings.push('Рекомендуется включить strict: true')
  }

  // jsx требует react или react-jsx
  if (config.jsx && !['react', 'react-jsx', 'react-jsxdev', 'preserve'].includes(config.jsx)) {
    errors.push(`Неверное значение jsx: "${config.jsx}"`)
  }

  return { valid: errors.length === 0, errors, warnings }
}

// --- Демонстрация ---

console.log('=== Профиль Node.js ===')
const nodeConfig = mergeConfigs(CONFIG_PROFILES.base, CONFIG_PROFILES.node)
console.log(JSON.stringify(nodeConfig, null, 2))

console.log('\n=== Профиль React ===')
const reactConfig = mergeConfigs(CONFIG_PROFILES.base, CONFIG_PROFILES.react)
console.log('target:', reactConfig.target)
console.log('jsx:', reactConfig.jsx)
console.log('lib:', reactConfig.lib)

console.log('\n=== Валидация ===')
const badConfig = { module: 'CommonJS', target: 'ESNext', jsx: 'invalid' }
const { valid, errors, warnings } = validateConfig(badConfig)
console.log('valid:', valid)
console.log('errors:', errors)
console.log('warnings:', warnings)

const goodConfig = mergeConfigs(CONFIG_PROFILES.base, CONFIG_PROFILES.node)
const result = validateConfig(goodConfig)
console.log('\nNode config valid:', result.valid)
console.log('warnings:', result.warnings)