Питання на співбесіді: Конфігурація й інструменти
Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.
14 питань
tsconfig.json у корені проєкту каже компілятору TypeScript, які файли перевіряти і як це робити. Каталог з цим файлом вважається коренем проєкту.
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"compilerOptions": {
"target": "es2022",
"module": "esnext",
"moduleResolution": "bundler",
"strict": true,
"noEmit": true,
"types": ["vite/client"],
"paths": { "@/*": ["./resources/js/*"] }
},
"include": ["resources/js/**/*.ts", "resources/js/**/*.vue"],
"exclude": ["node_modules", "public"]
}
Основні розділи:
include/exclude/files- які файли входять у проєкт.includeприймає шаблони,excludeвідкидає частину з них,files- явний список;compilerOptions- як перевіряти й що генерувати: строгість, цільова версія JavaScript, система модулів, шляхи, глобальні типи;extends- успадкувати базовий конфіг (свій спільний або з пакета на кшталт@tsconfig/strictest,@vue/tsconfig);references- посилання на інші проєкти в монорепозиторії.
Важливо розуміти, хто читає tsconfig.json:
tsc- повністю;- редактор (мовний сервер TypeScript) - для підказок і помилок;
- збирачі (Vite, esbuild) - лише частину опцій, і не перевіряють типи;
- Node.js з вбудованим зняттям типів - не читає взагалі.
noEmit: true - типова опція для застосунків на Vite: JavaScript генерує збирач, а tsc лише перевіряє типи.
Нові значення за замовчуванням (TypeScript 6.0+ і 7.0): strict увімкнено, module - esnext, target - останній стабільний стандарт ECMAScript, types - порожній список. Порожній tsconfig.json сьогодні - вже доволі строга конфігурація.
Практична порада: не копіювати чужий конфіг цілком, а починати з базового пресету фреймворку й змінювати лише те, що розумієте, - багато «магічних» опцій зі старих статей у TypeScript 7 уже видалено.
strict - не одна перевірка, а набір строгих опцій, увімкнених разом:
noImplicitAny- помилка, якщо тип не можна вивести і він став биany(параметри функцій без типів);strictNullChecks-nullіundefined- окремі типи.stringне може бутиnull, і компілятор змушує перевірити значення перед використанням. Найважливіша з усіх;strictFunctionTypes- коректна (контраваріантна) перевірка параметрів функцій;strictBindCallApply- перевірка аргументівbind,call,apply;strictPropertyInitialization- поля класу мають бути ініціалізовані в конструкторі;noImplicitThis- помилка наthisз неявним типомany;useUnknownInCatchVariables- змінна вcatchмає типunknown, а неany;alwaysStrict- режим'use strict'у кожному файлі (у TypeScript 7 його вже не можна вимкнути).
function greet(user: { name: string } | null) {
return user.name; // з strictNullChecks: помилка 'user' is possibly 'null'
}
З TypeScript 6.0 strict увімкнено за замовчуванням. Проєкт, що покладався на старе значення false, має явно вказати "strict": false - і це сигнал, що варто планувати перехід.
Чому не вимикати:
- без
strictNullChecksTypeScript мовчить про найпоширеніші помилки виконання -Cannot read properties of undefined; - без
noImplicitAnyзначна частина коду фактично не типізована; - нові опції строгості з'являються в наборі з новими версіями - проєкт зі
strict: trueотримує їх автоматично.
Міграція великого проєкту, де одразу тисячі помилок:
- вмикати опції по одній (
strict: false+ окремі прапорці), починаючи зnoImplicitAnyіstrictNullChecks; - або вмикати
strictдля нових каталогів через окремі конфіги чи інструменти поступової міграції.
Понад strict корисні noUncheckedIndexedAccess, exactOptionalPropertyTypes, noImplicitOverride, noFallthroughCasesInSwitch - вони в набір strict не входять і вмикаються окремо.
Ці три опції часто плутають, хоча вони відповідають на різні питання.
target - у яку версію JavaScript перетворювати синтаксис.
const user = data?.user ?? guest;
З target: "es2019" компілятор перепише ?. і ?? старим синтаксисом, з es2020 і новіше - лишить як є. target змінює лише синтаксис, а не додає відсутні функції (поліфіли).
У TypeScript 6.0+ значення за замовчуванням - останній стабільний стандарт (зараз es2025), а es5 у TypeScript 7 видалено - найнижча ціль тепер es2015. Для старих браузерів TypeScript-код компілюють іншим інструментом.
lib - які вбудовані API існують у середовищі, для перевірки типів:
"lib": ["es2025", "dom", "dom.iterable"]
Без dom компілятор не знає про document і window; без свіжого es20xx - про Array.prototype.toSorted чи Object.groupBy. Якщо lib не вказано, він береться з target (плюс dom). Для коду в Node.js dom зайвий, натомість потрібен пакет @types/node.
Пастка: lib лише описує типи. Вказати es2025, а запускати код у браузері, який цих методів не має, - помилка виконання, яку TypeScript не впіймає.
module - яку систему модулів генерувати (і як трактувати import/export):
esnext/preserve- лишити ES-модулі (типово для коду, який збирає Vite);nodenext- як Node.js: ES-модулі чи CommonJS залежно від"type"уpackage.jsonі розширень файлів;commonjs-require/module.exports.
amd, umd, systemjs і none у TypeScript 7 вже не підтримуються.
Пов'язана опція moduleResolution - як знаходити файли за шляхом в import: bundler для Vite, nodenext для коду, що виконує Node.js.
Для типового Vite-застосунку: target і module мало впливають на результат (код генерує збирач), але lib визначає, які API бачить редактор, - його варто узгодити з браузерами, які ви підтримуєте.
Замість import Button from '../../../components/Button.vue' зручно писати import Button from '@/components/Button.vue'. Для цього налаштовують псевдоніми шляхів.
У tsconfig.json:
{
"compilerOptions": {
"paths": {
"@/*": ["./resources/js/*"]
}
}
}
Важлива зміна: раніше разом з paths вказували baseUrl. У TypeScript 6.0 його оголосили застарілим, а в TypeScript 7 - видалено (помилка «Option 'baseUrl' has been removed»). Шляхи в paths тепер пишуть відносно каталогу з tsconfig.json, з явним префіксом ./.
Чому однієї опції paths замало: вона лише каже компілятору, де шукати типи. Код вона не змінює - у зібраному JavaScript лишиться import ... from '@/components/Button.vue', і хтось має перетворити цей шлях на справжній.
Тому той самий псевдонім треба налаштувати в інструменті, що виконує чи збирає код:
- Vite -
resolve.aliasуvite.config.ts(у Laravel-проєктах@часто вже налаштовано плагіном чи шаблоном стартового набору):
import { fileURLToPath, URL } from 'node:url';
export default defineConfig({
resolve: {
alias: { '@': fileURLToPath(new URL('./resources/js', import.meta.url)) },
},
});
- Vitest - бере
resolve.aliasз конфігурації Vite; - Node.js з вбудованим запуском TypeScript - не читає
tsconfig.jsonіpathsне підтримує. Альтернатива - subpath imports уpackage.json("imports": { "#/*": "./src/*" }), які розуміють і Node.js, і TypeScript.
Типові помилки:
- псевдонім є в
tsconfig.json, але не у збирачі - редактор задоволений, а збірка падає з «Failed to resolve import»; - різні значення в двох місцях - редактор показує один файл, а в зібраному коді опиняється інший;
pathsу бібліотеці, яку публікують на npm, - у споживачів ці шляхи не працюватимуть; у пакетах кращеimports/exportsзpackage.json.
Глобальні змінні середовища - process, Buffer, describe, it, expect - TypeScript знає не сам, а з пакетів типів: @types/node, @types/jest тощо. Опція types визначає, які з таких пакетів підключати глобально, без явного імпорту.
Що змінилося. До TypeScript 5.9 включно значення за замовчуванням було «усі пакети з node_modules/@types». У великих проєктах це сотні пакетів, підтягнутих транзитивно, - повільна перевірка й випадкові глобальні типи.
З TypeScript 6.0 (і в 7.0) types за замовчуванням - порожній список []. Після оновлення проєкт, що покладався на автоматичне підключення, бачить:
error TS2591: Cannot find name 'process'. Do you need to install type definitions for node?
Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
Рішення - перелічити потрібні пакети явно:
{
"compilerOptions": {
"types": ["node", "vite/client"]
}
}
node- для коду, що працює в Node.js (конфіги, скрипти, SSR);vite/client- типиimport.meta.env,import.meta.hotі імпорту ресурсів (.svg,.css) у Vite-застосунку;vitest/globals- якщо тести використовують глобальніdescribe/it.
Повернути стару поведінку можна значенням ["*"], але документація TypeScript радить явний список: за їхніми даними, багато проєктів пришвидшили збірку на 20-50% лише завдяки цьому.
Що варто знати:
typesстосується лише глобальних оголошень. Пакети, які ви імпортуєте (import express from 'express'), підключаються через імпорт і не потребують запису вtypes;- різні частини проєкту - різні глобальні типи: код браузера не повинен бачити
process, а конфіги Vite -document. Тому шаблони Vite мають два конфіги (tsconfig.app.jsonіtsconfig.node.json) з різнимиtypesіlib; - пакет має бути встановлений: запис у
typesбез@types/nodeуdevDependenciesдасть помилку «Cannot find type definition file».
Обидві опції закривають «дірки», які лишає навіть strict, тому їх часто вмикають додатково.
noUncheckedIndexedAccess - доступ за індексом чи довільним ключем може повернути undefined:
const items: string[] = [];
const first = items[0]; // без опції: string, з опцією: string | undefined
first.toUpperCase(); // з опцією: помилка 'first' is possibly 'undefined'
const prices: Record<string, number> = {};
prices['coffee'].toFixed(2); // з опцією: помилка
Без опції TypeScript вважає, що елемент масиву чи значення словника завжди є, - і items[0] на порожньому масиві падає під час виконання.
Поведінка, яку варто знати:
for...of,map,forEachне потребують перевірок - елемент там точно існує;- кортежі з відомою довжиною (
[string, number]) не уражені; Map.get()і так повертаєT | undefinedнезалежно від опції.
exactOptionalPropertyTypes - розрізняє «властивості немає» і «властивість є і дорівнює undefined»:
type Options = { theme?: 'dark' | 'light' };
const a: Options = {}; // гаразд
const b: Options = { theme: undefined }; // з опцією: помилка
Це важливо, бо в JavaScript ці стани поводяться по-різному: 'theme' in options, Object.keys, розгортання { ...defaults, ...options } - явний undefined перезапише значення за замовчуванням. Якщо undefined справді допустиме, його вказують явно: theme?: 'dark' | 'light' | undefined.
Чому вони не в strict:
- ціна для наявного коду: увімкнення в старому проєкті дає сотні помилок, частина з яких - перевірки там, де розробник «знає», що значення є;
- шум:
arr[i]у звичайному цикліforз індексом теж вимагає перевірки чи!; - сумісність з бібліотеками:
exactOptionalPropertyTypesвиявляє розбіжності в типах сторонніх пакетів.
Рекомендація: у нових проєктах вмикати обидві (пресет @tsconfig/strictest так і робить). У наявних - noUncheckedIndexedAccess у першу чергу: він ловить реальні помилки «undefined is not an object».
moduleResolution визначає, як TypeScript шукає файл за рядком в import - і тим самим, чи збігатиметься його уявлення з тим, як код справді виконається.
bundler - для коду, який збирає Vite, webpack, esbuild чи виконує Bun:
import { formatPrice } from './utils'; // без розширення - гаразд
import Button from '@/components/Button.vue';
- розширення файлів можна не вказувати;
- підтримує поле
exportsуpackage.jsonзалежностей; - поєднується з
module: "esnext"або"preserve"(з TypeScript 6.0 - і зcommonjs).
nodenext - для коду, який напряму виконує Node.js (сервер, CLI, бібліотеки для Node):
import { formatPrice } from './utils.js'; // розширення обов'язкове
- точно моделює Node.js: ES-модуль чи CommonJS визначається полем
"type"уpackage.jsonі розширенням (.mts,.cts); - розширення обов'язкові у відносних імпортах ES-модулів, як вимагає Node.js;
- іде в парі з
module: "nodenext".
Чому розширення .js, якщо файл .ts: TypeScript не переписує шляхи в імпортах, а після компіляції поруч буде utils.js. Для коду, що виконується через вбудоване зняття типів Node.js, використовують .ts в імпортах разом з опцією rewriteRelativeImportExtensions (для генерації .js) або allowImportingTsExtensions (якщо JavaScript не генерується).
Що обрати:
| Код | moduleResolution |
|---|---|
| фронтенд на Vite (Laravel + Vue/React) | bundler |
| сервер чи скрипти, що запускає Node.js | nodenext |
| бібліотека для npm | nodenext - так перевірка суворіша й результат працює всюди |
Застарілі варіанти: node (він же node10) моделював Node.js 10 і не знав про exports; classic - алгоритм з часів до Node.js. Обидва в TypeScript 7 видалено - помилка «Option 'moduleResolution=node10' has been removed».
Ознака неправильного вибору: TypeScript без помилок перевіряє імпорт, який падає під час виконання (або навпаки). bundler для коду, що запускає Node.js, - типова причина.
Vite, esbuild, oxc, swc і Node.js перетворюють TypeScript на JavaScript по одному файлу і без перевірки типів - просто прибирають анотації. Це швидко, але такий інструмент не бачить інших файлів проєкту. Частина конструкцій TypeScript без знання інших файлів перетворюється неправильно.
Головна проблема - імпорт типу:
// types.ts
export type User = { id: number };
// app.ts
import { User } from './types';
Компілятор TypeScript знає, що User - тип, і прибере імпорт. Інструмент, що бачить лише app.ts, цього не знає: залишить import { User } from './types' - і в браузері помилка «The requested module does not provide an export named 'User'».
isolatedModules - TypeScript попереджає про код, який неможливо безпечно перетворити по одному файлу: реекспорт типу без type, const enum між файлами, файли без імпортів і експортів.
verbatimModuleSyntax - строгіший і простіший підхід: імпорти лишаються в JavaScript рівно так, як написані, крім позначених type. Тому тип треба явно позначити:
import type { User } from './types';
import { fetchUser, type UserFilter } from './api';
З опцією TypeScript видасть помилку на import { User }, якщо User - лише тип.
Що обрати: у сучасних проєктах - verbatimModuleSyntax: true. Він робить поведінку однаковою для tsc, збирачів і Node.js і замінює старіші importsNotUsedAsValues і preserveValueImports. Шаблони Vite вмикають його за замовчуванням.
Наслідки, які варто знати:
- імпорти з побічними ефектами зберігаються:
import './styles.css'нікуди не зникне; import typeне виконує модуль - якщо вам потрібен побічний ефект модуля, потрібен звичайний імпорт;- з CommonJS опція змушує писати
import x = require('...')для CommonJS-виводу - у кодовій базі на ES-модулях це не відчувається; - лінтер (
@typescript-eslint/consistent-type-imports) автоматично виправляє імпорти, тож перехід на велику кодову базу - один прогін автовиправлення.
Зв'язок з Node.js: вбудоване виконання TypeScript у Node.js теж прибирає лише імпорти з type - без verbatimModuleSyntax такі помилки виявляться лише під час запуску.
Vite лише прибирає типи - перетворює TypeScript на JavaScript без перевірки. Код з помилкою типу const n: number = 'text' спокійно збереться й запуститься. Це навмисне рішення: перевірка типів - повільна операція, що потребує аналізу всього проєкту, а Vite перетворює файли по одному за мілісекунди.
Розподіл обов'язків:
- редактор (мовний сервер TypeScript, для Vue - розширення Vue - Official) показує помилки під час написання коду;
- окрема команда перевірки - у збірці й CI.
Типова конфігурація package.json:
{
"scripts": {
"dev": "vite",
"build": "tsc --noEmit && vite build",
"typecheck": "tsc --noEmit"
}
}
tsc --noEmit- перевірити типи без генерації файлів;- для Vue -
vue-tsc --noEmit: звичайнийtscне розуміє.vue-файли й не перевіряє шаблони; - для проєкту з кількома
tsconfig(застосунок і конфіги Node) -tsc -b.
Якщо перевірка типів у build надто сповільнює збірку, її виносять в окремий крок CI, що виконується паралельно зі збиранням.
Помилки типів під час розробки в браузері: плагін vite-plugin-checker запускає перевірку в окремому процесі й показує помилки поверх сторінки.
TypeScript 7 змінює баланс. Нативний компілятор перевіряє великий проєкт у 8-12 разів швидше - перевірка в build і в режимі --watch стає майже непомітною. Але TypeScript 7.0 ще не має програмного API, а інструменти, що його використовують (typescript-eslint підтримує лише TypeScript до 6.x, vue-tsc теж працює через API компілятора), потребують TypeScript 6 - тому в проєкті може знадобитися пакет сумісності @typescript/typescript6 поряд із TypeScript 7.
Наслідки «перевірки лише в редакторі»:
- помилки в файлах, які ніхто не відкривав, лишаються непоміченими;
- зміна типу в одному місці ламає десятки інших файлів - редактор покаже це лише у відкритих;
- тому перевірка в CI обов'язкова, навіть якщо в команді всі користуються редактором з підтримкою TypeScript.
Обмеження, що випливають з покофайлового перетворення, - isolatedModules/verbatimModuleSyntax у tsconfig.json, щоб TypeScript попереджав про конструкції, які Vite перетворить неправильно.
Переписувати все одразу ризиковано й довго. TypeScript дозволяє змішувати JavaScript і TypeScript в одному проєкті й переходити по файлу.
Крок 1 - tsconfig.json з дозволом JavaScript:
{
"compilerOptions": {
"allowJs": true,
"checkJs": false,
"strict": false,
"noEmit": true
},
"include": ["resources/js"]
}
allowJs-.js-файли входять у проєкт: TypeScript-файли можуть їх імпортувати, редактор дає підказки;checkJs- перевіряти й.js-файли. Можна вмикати точково - коментарем// @ts-checkна початку окремого файлу.
Крок 2 - типи в JavaScript через JSDoc (без перейменування файлів):
// @ts-check
/**
* @param {number} amount
* @param {'UAH' | 'USD'} currency
* @returns {string}
*/
export function formatPrice(amount, currency) { /* ... */ }
/** @typedef {{ id: number, name: string }} User */
Крок 3 - перейменування файлів у .ts - починаючи з «листових» модулів без залежностей (утиліти, константи, API-клієнт), потім угору до компонентів.
Крок 4 - посилення строгості: спершу noImplicitAny, потім strictNullChecks, наприкінці повний strict.
Що змінилося в TypeScript 7 для JavaScript-файлів. Аналіз JSDoc став ближчим до звичайного TypeScript, частина старих конструкцій більше не розпізнається:
@enumне має особливого значення - потрібен@typedefнад(typeof Obj)[keyof typeof Obj];- синтаксис Closure (
function(string): void) замінюється на(s: string) => void; - одинокий
?як тип і постфіксний!не підтримуються; @classне робить функцію конструктором - потрібенclass.
Тому JSDoc у старих проєктах після оновлення може дати нові помилки.
Практичні поради:
- не змінювати поведінку разом з типами - міграція окремими комітами, без рефакторингу логіки;
anyдозволений як тимчасовий, але з позначкою (// TODO: тип) - і лінтер, що рахує їх кількість;- типи для відповідей API - одне з перших, що варто додати: вони дають найбільше користі;
@ts-expect-errorкраще за@ts-ignore- він сам повідомить, коли помилку виправлено і коментар можна прибрати.
TypeScript 7 - компілятор, переписаний з TypeScript на Go. Порт робили максимально близько до оригіналу, тож перевірка типів дає ті самі результати, а виграш - у швидкості: нативний код і багатопотоковість.
Що це дає на практиці:
- повна збірка у 8-12 разів швидша: за даними команди TypeScript, перевірка кодової бази VS Code - 10,6 с замість 125,7 с;
- редактор: проєкт відкривається й показує першу помилку за секунду-дві замість десятків секунд;
- пам'ять - зазвичай менше, ніж у TypeScript 6;
- новий режим
--watchна основі файлового спостерігача з Parcel, портованого на Go.
Нові прапорці паралелізму:
--checkers N- кількість паралельних перевіряльників типів (за замовчуванням 4). Більше - швидше на потужних машинах, але більше пам'яті; на слабких CI-раннерах варто зменшити;--builders N- паралельна збірка проєктів у--build(монорепозиторії). Множиться з--checkers:4 × 4- до 16 перевіряльників одночасно;--singleThreaded- вимкнути паралелізм (налагодження, обмежені середовища).
Різна кількість --checkers у рідкісних випадках може дати різні результати, залежні від порядку, - тому команді варто зафіксувати одне значення для всіх середовищ.
Що потребує уваги при переході:
- TypeScript 7 бере значення за замовчуванням з 6.0 (
strict,types: [],rootDir: ".") і робить помилками все, що 6.0 оголосив застарілим:baseUrl,moduleResolution: node10,target: es5,module: amd/umd,esModuleInterop: falseта інше. Рекомендований шлях - спершу перейти на 6.0 і прибрати всі попередження; - немає програмного API в 7.0 - його обіцяють у 7.1. Інструменти, що імпортують
typescriptяк бібліотеку (typescript-eslint, частина плагінів збирачів), працюють через пакет сумісності@typescript/typescript6, встановлений поряд через псевдонім npm. Тодіtsc- це TypeScript 7, а інструменти бачать API 6.0; - JSDoc у JavaScript-файлах аналізується ближче до
.ts- деякі старі шаблони перестали розпізнаватися; - шаблонні рядкові типи тепер працюють з кодовими точками Unicode, а не з половинками сурогатних пар.
Що лишилося незмінним: мова, синтаксис і правила перевірки. Код, що компілюється в 6.0 без попереджень, компілюється в 7.0 так само.
Сучасний Node.js виконує .ts-файли без окремої збірки: він замінює анотації типів пробілами (type stripping) і запускає те, що лишилося.
node scripts/import-vacancies.ts
Зняття типів увімкнене за замовчуванням з Node.js 22.18 / 23.6 і стабільне з 24.12 / 25.2. Перевірки типів при цьому немає - помилки типів не зупинять запуск.
Обмеження - лише «стиранний» синтаксис. Node.js не генерує код, він тільки прибирає типи. Конструкції TypeScript, що створюють JavaScript, не підтримуються:
enum;namespaceз кодом під час виконання;- параметри-властивості в конструкторі (
constructor(private repo: Repo)); import x = require(...)і псевдоніми імпорту;- декоратори (поки їх немає в самому JavaScript).
Прапорець --experimental-transform-types, що їх перетворював, у Node.js 26 прибрали.
erasableSyntaxOnly (з TypeScript 5.8) змушує tsc повідомляти про такі конструкції заздалегідь:
enum Status { Draft, Published } // error TS1294: This syntax is not allowed when 'erasableSyntaxOnly' is enabled
Замість enum - об'єкт з as const і тип-об'єднання; замість параметрів-властивостей - звичайні поля.
Рекомендовані Node.js налаштування tsconfig.json:
{
"compilerOptions": {
"noEmit": true,
"target": "esnext",
"module": "nodenext",
"rewriteRelativeImportExtensions": true,
"erasableSyntaxOnly": true,
"verbatimModuleSyntax": true
}
}
Інші особливості:
- розширення обов'язкові:
import { x } from './utils.ts'; import typeдля типів - інакше Node.js залишить імпорт і впаде (томуverbatimModuleSyntax);tsconfig.jsonігнорується:pathsне працюють - замість них subpath imports уpackage.json("#/*");- файли в
node_modulesне виконуються - пакети мають публікуватися як JavaScript; .tsxне підтримується.
Коли це доречно: скрипти, утиліти, інструменти розробки, невеликі сервіси - там, де збірка була зайвим кроком. Для повної підтримки TypeScript (paths, enum, JSX) - інструменти на кшталт tsx або звичайна збірка.
Великий кодовий масив в одному tsconfig.json перевіряється цілком при кожній зміні. Project references ділять його на окремі проєкти з явними залежностями, і TypeScript перевіряє лише змінене та залежне від нього.
Структура:
// tsconfig.json (корінь) - лише посилання
{
"files": [],
"references": [
{ "path": "./packages/shared" },
{ "path": "./packages/web" },
{ "path": "./packages/admin" }
]
}
// packages/shared/tsconfig.json
{
"compilerOptions": {
"composite": true,
"declaration": true,
"rootDir": "./src",
"outDir": "./dist"
}
}
// packages/web/tsconfig.json
{
"references": [{ "path": "../shared" }]
}
tsc -b # зібрати всі проєкти в порядку залежностей
tsc -b --watch
composite: true - вимоги до проєкту, на який посилаються: генерація .d.ts, явний rootDir, усі файли в include. Залежний проєкт бачить лише оголошення (.d.ts) залежності, а не її вихідний код - тому перевірка швидша.
incremental - зберегти результати попередньої перевірки у файлі .tsbuildinfo і наступного разу перевіряти лише змінене. Для composite увімкнено автоматично.
Що це дає:
- швидкість: зміна в
adminне змушує перевірятиweb; - межі: пакет не може імпортувати з іншого, якщо на нього немає посилання, - архітектура перевіряється компілятором;
- різні налаштування для частин: код браузера з
lib: ["dom"], конфіги й сервер - з типами Node.js. Саме так шаблон Vite ділить проєкт наtsconfig.app.jsonіtsconfig.node.json.
TypeScript 7 збирає незалежні проєкти паралельно (прапорець --builders), тож виграш від поділу ще більший. Обмежувач - граф залежностей: проєкт збирається лише після тих, від яких залежить. Опція isolatedDeclarations дає змогу генерувати .d.ts без перевірки типів залежностей і розпаралелити й це.
Пастки:
rootDirз TypeScript 6.0 за замовчуванням - каталог зtsconfig.json. Якщо вихідні файли вsrc/, аrootDirне вказано, результат опиниться вdist/src/...замістьdist/...;- застарілі
.d.ts: редактор може показувати старі типи залежності, доки її не перезібрано; skipLibCheck(пропустити перевірку.d.ts) теж помітно пришвидшує збірку, але ховає помилки в оголошеннях власних пакетів - його вмикають свідомо.
TypeScript 6.0 - перехідний реліз: останній на старому компіляторі, який готує проєкти до TypeScript 7. Він змінює значення за замовчуванням і оголошує застарілими опції, які в 7.0 стали помилками.
Нові значення за замовчуванням:
| Опція | Було | Стало |
|---|---|---|
strict |
false |
true |
module |
залежно від target |
esnext |
target |
es5 |
останній стабільний стандарт (es2025) |
types |
усі @types/* |
[] |
rootDir |
спільний каталог вихідних файлів | каталог з tsconfig.json |
noUncheckedSideEffectImports |
false |
true |
Найчастіші проблеми після оновлення:
- «Cannot find name 'process'» / «describe» - додати
"types": ["node", ...]; - результат збирається в
dist/src/index.jsзамістьdist/index.js- вказати"rootDir": "./src"; - нові помилки строгості - або виправляти, або явно
"strict": falseяк тимчасовий крок.
Видалено в TypeScript 7 (у 6.0 - попередження, які можна приглушити "ignoreDeprecations": "6.0"):
baseUrl- шляхи вpathsтепер відносно каталогу конфігу з префіксом./;moduleResolution: node(node10) іclassic- замінити наbundlerабоnodenext;target: es5іdownlevelIteration- найнижча цільes2015; для ES5 - зовнішній компілятор;module: amd,umd,systemjs,noneіoutFile;esModuleInterop: false,allowSyntheticDefaultImports: false,alwaysStrict: false;- ключове слово
moduleдля просторів імен (лишеnamespace) іassertsв імпортах (лишеwith); - передача файлів у
tscу каталозі зtsconfig.jsonбез--ignoreConfig.
Рекомендований порядок:
- оновитися до TypeScript 6.0 і виправити всі попередження про застарілі опції (не приглушувати
ignoreDeprecationsнадовго - у 7.0 це не спрацює); - частину змін (
baseUrl,rootDir) робить автоматично експериментальний інструментts5to6; - перевірити з прапорцем
stableTypeOrdering- у 7.0 він увімкнений і незмінний; з ним 6.0 дає ті самі результати, що й 7.0; - перейти на TypeScript 7, а для інструментів, що потребують API (
typescript-eslint), лишити поряд@typescript/typescript6.
Чому ці зміни корисні: явний types пришвидшує перевірку на 20-50%, а видалені опції здебільшого ховали помилки (TypeScript «бачив» імпорти, які не працювали під час виконання).