Пошук уроків, статей та іншого контенту
Навчитеся розрізняти синтаксичні, типові, компіляційні та помилки виконання у TypeScript і правильно їх діагностувати.
У TypeScript помилка може виникнути на різних етапах:
Під час синтаксичного аналізу — код не відповідає граматиці TypeScript.
Під час перевірки типів — синтаксис правильний, але значення використовуються несумісно з їхніми типами.
Під час компіляції — TypeScript не може коректно перетворити код у JavaScript або зупиняє генерацію файлів через налаштування.
Під час виконання — згенерований JavaScript запускається, але виникає виняток або неправильна поведінка.
TypeScript допомагає знайти багато проблем до запуску програми, але не може виявити всі помилки виконання.
Синтаксична помилка означає, що парсер не може зрозуміти структуру коду.
Наприклад:
function greet(name: string {
return `Привіт, ${name}`;
}Після string пропущено закривальну дужку ). Через це TypeScript не може правильно розібрати оголошення функції.
Компілятор покаже повідомлення приблизно такого вигляду:
error TS1005: ')' expected.Синтаксичні помилки зазвичай спричиняють:
пропущені дужки або лапки;
зайві чи пропущені коми;
неправильне використання ключових слів;
незакриті блоки коду;
некоректний синтаксис типів або узагальнених параметрів.
Поки синтаксична помилка не виправлена, наступні повідомлення компілятора можуть бути вторинними. Одна пропущена дужка іноді спричиняє багато помилок у наступних рядках.
Почніть із першого повідомлення про помилку.
Перевірте рядок, на який вказує компілятор.
Додатково перевірте кілька рядків перед ним: помилка часто виникає саме там.
Перевірте баланс дужок, фігурних дужок, квадратних дужок і лапок.
Після виправлення запустіть перевірку повторно.
Типова помилка виникає, коли код синтаксично правильний, але не відповідає системі типів.
let age: number = "25";Рядок "25" не можна присвоїти змінній типу number.
Компілятор повідомить:
error TS2322: Type 'string' is not assignable to type 'number'.Інший приклад:
function add(a: number, b: number): number {
return a + b;
}
add(10, "5");Функція очікує два числа, але другим аргументом передано рядок.
let isActive: boolean = "yes";Тип string несумісний із типом boolean.
type User = {
id: number;
name: string;
};
const user: User = {
id: 1
};Обов'язкова властивість name відсутня.
function printLength(value: string): void {
console.log(value.length);
}
printLength(42);Функція приймає лише рядок, але отримує число.
undefinedfunction findUserName(names: string[], index: number): string {
return names[index].toUpperCase();
}За суворих налаштувань TypeScript може повідомити, що результат names[index] потенційно має значення undefined.
Безпечніший варіант:
function findUserName(
names: string[],
index: number
): string | undefined {
const name = names[index];
return name?.toUpperCase();
}Тепер тип результату явно враховує відсутність елемента.
Компіляційна помилка — це проблема, через яку компілятор не може виконати компіляцію відповідно до поточних правил і налаштувань.
Типові помилки аналізу коду — синтаксичні та типові — також повідомляються під час компіляції. Тому термін «компіляційна помилка» часто використовують як ширше поняття для будь-якої помилки, яку знаходить tsc до запуску програми.
Наприклад, перевірити файл без створення JavaScript можна командою:
tsc --noEmitПараметр --noEmit означає: перевірити TypeScript, але не записувати згенеровані файли.
Якщо проєкт використовує tsconfig.json, команда застосує налаштування з цього файлу:
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"strict": true,
"noEmitOnError": true,
"outDir": "dist"
},
"include": ["src"]
}Важливе налаштування noEmitOnError:
якщо воно має значення true, JavaScript не буде створено, якщо є помилки;
якщо воно не ввімкнене, TypeScript у деяких випадках може згенерувати JavaScript навіть за наявності діагностичних повідомлень.
Для навчання та розробки корисно спочатку виконувати:
tsc --noEmitТак можна перевірити код, не змінюючи папку зі зібраними файлами.
Повідомлення зазвичай містить:
src/user.ts(12,7): error TS2322: Type 'string' is not assignable to type 'number'.У ньому вказано:
файл: src/user.ts;
номер рядка: 12;
номер стовпця: 7;
категорію: error;
код діагностики: TS2322;
опис проблеми.
Номер діагностики допомагає зрозуміти, який саме тип проблеми знайшов компілятор. Водночас важливо читати не лише код, а й текст повідомлення та контекст рядка.
Помилка виконання виникає після того, як код успішно пройшов синтаксичну та типову перевірку і був запущений як JavaScript.
Наприклад:
function getFirstItem(items: string[]): string {
return items[0].toUpperCase();
}
console.log(getFirstItem([]));За типами функція приймає масив рядків. Однак порожній масив не має першого елемента. Після компіляції під час виконання може виникнути:
TypeError: Cannot read properties of undefinedTypeScript не завжди може визначити, які дані фактично надійдуть у програму під час запуску.
Безпечна реалізація:
function getFirstItem(items: string[]): string | undefined {
return items[0]?.toUpperCase();
}
console.log(getFirstItem([]));Тепер програма поверне undefined, а не спробує викликати метод у відсутнього значення.
доступ до властивості undefined або null;
виклик значення, яке не є функцією;
ділення або обчислення з неочікуваними даними;
помилка під час роботи з мережею чи файловою системою;
виняток, кинутий сторонньою бібліотекою;
невідповідність між типами, описаними в коді, і фактичними зовнішніми даними.
TypeScript перевіряє код до виконання, але не перевіряє автоматично кожне значення, яке приходить із зовнішнього джерела.
Наприклад:
type User = {
id: number;
name: string;
};
const rawData: unknown = JSON.parse(
'{"id":"not-a-number","name":"Олена"}'
);
const user = rawData as User;
console.log(user.id.toFixed(2));Оператор as User не перетворює дані та не перевіряє їх. Він лише повідомляє компілятору: «вважай це значення типом User».
У цьому прикладі фактичне значення id є рядком, тому виклик toFixed спричинить помилку виконання.
Для даних із JSON, HTTP-запитів або інших зовнішніх джерел потрібна явна перевірка:
type User = {
id: number;
name: string;
};
function isUser(value: unknown): value is User {
if (typeof value !== "object" || value === null) {
return false;
}
const record = value as Record<string, unknown>;
return (
typeof record.id === "number" &&
typeof record.name === "string"
);
}
const rawData: unknown = JSON.parse(
'{"id":"not-a-number","name":"Олена"}'
);
if (isUser(rawData)) {
console.log(rawData.id.toFixed(2));
} else {
console.error("Отримано некоректні дані користувача");
}Тут значення спочатку має тип unknown. Воно стає доступним як User лише після успішної перевірки isUser.
Розглянемо програму, яка обчислює середнє значення чисел.
function average(values: number[]): number | undefined {
if (values.length === 0) {
return undefined;
}
const total = values.reduce(
(sum, value) => sum + value,
0
);
return total / values.length;
}
const scores = [10, 8, 9];
const result = average(scores);
if (result === undefined) {
console.log("Немає значень для обчислення");
} else {
console.log(`Середнє значення: ${result.toFixed(2)}`);
}Збережіть код у файлі average.ts і скомпілюйте:
tsc average.ts --strict --target ES2020Після успішної компіляції TypeScript створить average.js. Його можна запустити:
node average.jsОчікуваний результат:
Середнє значення: 9.00У цьому прикладі враховано можливу проблему виконання: середнє значення порожнього масиву не використовується як звичайне число.
Коли програма не працює, зручно перевіряти її в такому порядку.
Запустіть компілятор і виправте першу синтаксичну помилку. Не намагайтеся одразу виправити всі повідомлення — частина з них може зникнути після першого виправлення.
Прочитайте повідомлення повністю:
який тип очікувався;
який тип отримано;
у якому виразі виникла невідповідність;
чи може значення бути null або undefined.
Не варто бездумно додавати as або any. Спочатку з'ясуйте, чому типи не збігаються.
Переконайтеся, що запускається актуальний JavaScript-файл, а не стара версія. Якщо використовується outDir, перевірте відповідну папку.
Зверніть увагу на:
тип винятку, наприклад TypeError;
текст повідомлення;
стек викликів;
файл і рядок, де сталася помилка;
фактичні значення змінних у цьому місці.
Якщо значення походить від користувача, API, JSON або файлової системи, не покладайтеся лише на оголошений тип. Перевірте фактичну структуру даних.
| Етап | Приклад проблеми | Основний інструмент | |---|---|---| | Синтаксичний аналіз | пропущена дужка | повідомлення tsc | | Перевірка типів | рядок передано замість числа | TypeScript Compiler | | Компіляція | код не відповідає налаштуванням проєкту | tsc, tsconfig.json | | Виконання | доступ до властивості undefined | стек помилки, налагоджувач, логи |
Причина проблеми може бути в попередньому рядку, особливо для синтаксичних помилок. Перевіряйте контекст, а не тільки позначений символ.
anyconst value: any = getValue();any вимикає значну частину перевірок TypeScript. Це може приховати помилку під час компіляції, але збільшити ймовірність помилки виконання.
Краще описати правильний тип або використати unknown і виконати перевірку.
const city = user?.address?.city;Такий запис запобігає винятку, але може приховати причину відсутності даних. Якщо адреса є обов'язковою для бізнес-логіки, краще явно перевірити її та повідомити про некоректний стан.
Коментарі або налаштування, які вимикають перевірку, не виправляють проблему. Їх слід використовувати лише тоді, коли причина відома і має обґрунтування.
Оголошення const response: User не перевіряє відповідь сервера. Типи TypeScript існують переважно під час компіляції та не виконують автоматичну валідацію JSON.
Синтаксичні помилки не дають TypeScript розібрати структуру коду.
Типові помилки означають, що синтаксично правильні значення використовуються несумісно.
Компіляційні помилки виникають під час перевірки або генерації JavaScript і включають синтаксичні та типові проблеми.
Помилки виконання з'являються після запуску JavaScript.
Для перевірки проєкту без генерації файлів використовуйте tsc --noEmit.
Починайте діагностику з першого повідомлення компілятора.
Не використовуйте any і as як спосіб приховати невирішену проблему.
Дані із зовнішніх джерел потрібно перевіряти під час виконання, навіть якщо для них описано TypeScript-тип.