Многие проекты сталкиваются с техническим долгом из-за слабой начальной конфигурации TypeScript. Включение флага strict: true — это лишь необходимый минимум. Промышленный стандарт 2026 года требует включения дополнительных защитных опций: noUncheckedIndexedAccess, exactOptionalPropertyTypes и isolatedDeclarations. В связке с плоской конфигурацией ESLint v9 Flat Config и абсолютными путями алиасов (@/*) это гарантирует мгновенную обратную связь и нулевую толерантность к any.
Начиная разработку без жестких правил компилятора, команды неизбежно сталкиваются со следующими проблемами:
const item = users[10] имеет тип User, даже если в массиве всего 2 элемента. В рантайме обращение item.name приводит к фатальному падению TypeError: Cannot read properties of undefined. Опция noUncheckedIndexedAccess форсирует тип User | undefined, обязывая разработчика выполнить проверку.import { formatPrice } from '../../../../shared/utils/currency' ломаются при перемещении файлов и затрудняют рефакторинг. Настройка Path Aliases (@/shared/utils/currency) делает импорты стабильными и предсказуемыми.Сборка и валидация кода разделяются на два изолированных процесса: проверка типов (Type Checking) и транспиляция (Transpilation / Bundling):
[ Source Files: src/**/*.ts ]
│
┌────────────┴────────────┐
▼ ▼
[ TypeScript Compiler (tsc) ] [ Bundler / Runtime ]
(noEmit: true) (Vite / Rollup / esbuild / Node.js)
│ │
1. Строгая проверка типов 1. Удаление синтаксиса типов
2. Валидация контрактов 2. Быстрая транспиляция в JS
3. Детекция undefined 3. Генерация production-бандла
│ │
▼ ▼
Zero Type Errors dist/bundle.js (Ready to run)Создание package.json через pnpm init и установка актуальных версий TypeScript 5.8+, tsc-watch, @types/node и ESLint 9.
Настройка compilerOptions с включением strict, noUncheckedIndexedAccess, exactOptionalPropertyTypes и target ES2024.
Определение baseUrl и сопоставления путей paths: { '@/*': ['./src/*'] } для чистых импортов.
Создание eslint.config.mjs с подключением @typescript-eslint/parser и правил запрета any и console.
Добавление скриптов typecheck, dev, build и lint в package.json с поддержкой tsc-watch.
Команда включила флаг noEmit: true в tsconfig.json и запустила команду tsc в пайплайне CI/CD. Что произойдет на этом этапе сборки?
Разработчик пишет строго типизированный код с использованием путей @/shared, generic-функций и строгих типов.
Языковой сервер tsserver в редакторе мгновенно подсвечивает ошибки несоответствия контрактов.
Процесс tsc-watch компилирует инкрементальное дерево типов в оперативной памяти без записи файлов на диск (noEmit).
Использование tsconfig.tsbuildinfo сокращает время повторной проверки до менее 100 мс.
ESLint Flat Config с typescript-eslint анализирует AST-дерево на предмет неиспользуемых переменных, висячих промисов и неявных приведений.
Правило @typescript-eslint/no-floating-promises защищает от необработанных асинхронных сбоев.
Нативный стриппинг типов Node.js или быстрый сборщик генерирует результирующий исполняемый артефакт.
Полное разделение этапа проверки типов и рантайма исключает задержки при локальной разработке.
Ниже представлены готовые конфигурационные файлы эталонного проекта.
{
"compilerOptions": {
/* Базовые параметры таргетинга и модулей */
"target": "ES2024",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"lib": ["ES2024"],
"types": ["node"],
/* Режим компиляции и изоляция */
"noEmit": true,
"isolatedModules": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
/* Бескомпромиссный строгий режим */
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
/* Дополнительные проверки надежности 2026 */
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"useUnknownInCatchVariables": true,
/* Разрешение путей и алиасы */
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}import tsParser from '@typescript-eslint/parser';
import tsPlugin from '@typescript-eslint/eslint-plugin';
export default [
{
ignores: ['dist/**', 'node_modules/**', '.next/**', 'coverage/**']
},
{
files: ['src/**/*.{ts,tsx}'],
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: './tsconfig.json'
}
},
plugins: {
'@typescript-eslint': tsPlugin
},
rules: {
...tsPlugin.configs['recommended'].rules,
...tsPlugin.configs['recommended-requiring-type-checking'].rules,
// Строгие запреты WPSKILLS
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/explicit-function-return-type': 'warn',
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/no-misused-promises': 'error',
'@typescript-eslint/strict-boolean-expressions': 'warn',
'@typescript-eslint/no-unnecessary-condition': 'error',
// Общая гигиена кода
'no-console': ['warn', { allow: ['warn', 'error', 'info'] }],
'prefer-const': 'error'
}
}
];{
"name": "enterprise-typescript-starter",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "tsc-watch --noClear --onSuccess \"node dist/index.js\"",
"typecheck": "tsc --noEmit",
"typecheck:watch": "tsc --noEmit --watch",
"lint": "eslint src/",
"lint:fix": "eslint src/ --fix",
"build": "tsc --noEmit && tsup src/index.ts --format esm --dts --clean"
},
"devDependencies": {
"@types/node": "^24.0.0",
"@typescript-eslint/eslint-plugin": "^8.20.0",
"@typescript-eslint/parser": "^8.20.0",
"eslint": "^9.18.0",
"tsc-watch": "^6.2.1",
"tsup": "^8.3.5",
"typescript": "^5.8.2"
}
}Рассмотрим, как строгая конфигурация спасает от ошибок рантайма при работе со словарями и опциональными полями:
// src/user-registry.ts
export interface UserProfile {
readonly id: string;
name: string;
email: string;
avatarUrl?: string; // exactOptionalPropertyTypes запрещает передавать undefined явно
}
export class UserRegistry {
private users: Map<string, UserProfile> = new Map();
private userList: UserProfile[] = [];
public addUser(user: UserProfile): void {
this.users.set(user.id, user);
this.userList.push(user);
}
// noUncheckedIndexedAccess форсирует возврат UserProfile | undefined
public getByIndex(index: number): UserProfile | undefined {
return this.userList[index];
}
public getDisplayName(id: string): string {
const user = this.users.get(id);
// TypeScript обязывает проверить наличие user перед обращением к полям
if (!user) {
throw new Error(`User with ID ${id} was not found`);
}
return user.name;
}
}Разработчик включает noEmit: true в tsconfig.json, а затем запускает команду сборки tsc и удивляется, почему директория dist не создалась. Помните: noEmit: true отключает генерацию JS-файлов компилятором tsc. Для сборки бандла используйте специализированный инструмент (tsup, esbuild, vite), а tsc держите исключительно для быстрой проверки типов tsc --noEmit.
Разработчик прописывает @/* в paths tsconfig.json, компилирует проект, но при запуске Node.js выбрасывает ошибку Cannot find package '@/services/user'. Компилятор tsc не переписывает пути импортов в скомпилированном JS! Решение: использовать сборщик (tsup / vite), который заменяет алиасы на относительные пути при бандлинге, либо подключать пакет tsconfig-paths в рантайме.
При включенном флаге exactOptionalPropertyTypes следующий код вызовет ошибку компиляции:
interface Settings {
theme?: 'dark' | 'light';
}
const s: Settings = { theme: undefined }; // ОШИБКА: тип undefined не присваиваем типу 'dark' | 'light'Решение: если свойство может быть явно undefined, укажите это в типе theme?: 'dark' | 'light' | undefined, либо не передавайте ключ вовсе: const s: Settings = {};.
Запуск ts-node src/index.ts на продакшен-серверах приводит к колоссальному расходу оперативной памяти и задержкам старта, так как компиляция типов происходит на лету в процессе Node.js. В проде запускайте только предварительно скомпилированный чистый JavaScript код через node dist/index.js.
В проекте включен флаг noUncheckedIndexedAccess: true. Разработчик пишет код: const user = userList[0]; user.updateName('Alex');. Почему компилятор выдаст ошибку?