tsconfig.json говорит компилятору, какие файлы брать, насколько строго проверять и что выдавать на выходе. На собесе обычно спрашивают про strict, модули и .d.ts.
strict
"strict": true включает пачку флагов, главные из них:
strictNullChecks:nullиundefinedне входят в остальные типы.noImplicitAny: нельзя оставить параметр без типа, если его не вывести.strictFunctionTypes: параметры функций проверяются контравариантно (для методов, объявленных как методы, остаётся послабление).useUnknownInCatchVariables:catch (e)даётe: unknown.strictPropertyInitialization: поле класса обязано получить значение в конструкторе.alwaysStrict,strictBindCallApply,noImplicitThis.
Новые флаги семейства strict добавляются в strict в новых версиях, поэтому обновление TypeScript может дать новые ошибки.
Не входят в strict, но полезны: noUncheckedIndexedAccess (доступ по индексу даёт T | undefined), exactOptionalPropertyTypes, noImplicitOverride, noFallthroughCasesInSwitch.
Модули и разрешение импортов
moduleопределяет формат выходного кода и правила импорта:esnext,nodenext,preserve.moduleResolutionопределяет, как искать файлы:bundlerдля кода под Vite, webpack, Next;nodenextдля кода, который запустит Node.js. Старое значениеnode(node10) устарело.- При
nodenextв ESM нужно писать расширение в импорте:import { x } from './util.js'(именно.js, даже если файлutil.ts). esModuleInteropпозволяетimport fs from 'fs'для CommonJS-модулей.verbatimModuleSyntaxтребуетimport typeдля импортов, нужных только типам, и не переписывает импорты.
Что выдавать
target: до какой версии JS понижать синтаксис. Полифиллы TypeScript не добавляет.lib: какие встроенные типы доступны (dom,es2023). Проект на Node обычно безdom.noEmit: только проверка, сборку делает другой инструмент (Vite, esbuild, SWC). Так устроено большинство фронтенд-проектов.declaration: генерировать.d.tsрядом с JS, нужно для библиотек.isolatedModules: запрещает конструкции, которые нельзя собрать, глядя на один файл. Нужен, когда транспилирует esbuild или SWC.
Пути
paths задаёт алиасы ("@/*": ["./src/*"]). Это знание только для компилятора: сборщик должен понимать тот же алиас, иначе в рантайме импорт не найдётся.
Файлы деклараций
.d.ts содержат только типы, без кода. Откуда берутся:
- Поле
typesвpackage.jsonбиблиотеки. - Пакеты
@types/имяиз DefinitelyTyped. - Свои декларации в проекте.
// global.d.ts
declare module '*.svg' {
const src: string;
export default src;
}
declare global {
interface Window { ym?: (...args: unknown[]) => void }
}
export {};
declare говорит: значение существует, кода не генерируй. declare global внутри модуля расширяет глобальную область. Расширение модуля (module augmentation) добавляет поля в типы чужой библиотеки:
// в типах Express интерфейс Request объявлен в этом пакете
import 'express-serve-static-core';
declare module 'express-serve-static-core' {
interface Request { userId?: string }
}
Работает благодаря слиянию интерфейсов. Для Express есть и второй путь: глобальное пространство имён Express (declare global { namespace Express { interface Request { ... } } }).
skipLibCheck: true пропускает проверку всех .d.ts. Ускоряет сборку, но скрывает конфликты в типах зависимостей.
project references
references и composite разбивают монорепо на проекты, которые tsc -b собирает инкрементально и в правильном порядке.
Частые ошибки
- Ждать, что
target: es5добавит полифилл дляArray.prototype.at. - Настроить
pathsи не настроить алиас в сборщике. - Забыть
export {}в файле сdeclare global: без импорта или экспорта файл считается скриптом.
Что спросят на собесе
- Что включает
strictи какой флаг самый важный. - Чем
moduleResolution: bundlerотличается отnodenext. - Что такое
.d.tsи откуда TypeScript берёт типы библиотеки. - Как добавить поле в
Windowили вRequestиз Express. - Зачем
isolatedModulesиnoEmitв проекте на Vite.