Обробка винятків.
JavaScript має клас Error
, який можна використовувати для винятків. Ви видаєте помилку з ключовим словом throw
. Ви можете зловити його за допомогою пари блоків try
/ catch
, наприклад.
try {
throw new Error('Something bad happened');
}
catch(e) {
console.log(e);
}
Подтипи помилок.
Окрім вбудованого класу Error
, є кілька додаткових вбудованих класів помилок, які успадковують від Error
, які може викликати середовище виконання JavaScript:
Створює екземпляр, що представляє помилку, яка виникає, коли числова змінна або параметр виходить за межі допустимого діапазону.
// Виклик з перевіщенною кількістю аргументів
console.log.apply(console, new Array(1000000000)); // RangeError: Недійсна довжина масиву
Створює екземпляр, що представляє помилку, яка виникає під час використання недійсного посилання. Наприкллад:
'use strict';
console.log(notValidVar); // ReferenceError: notValidVar не визначено
Створює екземпляр, що представляє синтаксичну помилку, яка виникає під час аналізу коду, який не є дійсним JavaScript.
1***3; // SyntaxError: Несподіваний маркер *
Створює екземпляр, що представляє помилку, яка виникає, коли змінна або параметр має недійсний тип.
('1.2').toPrecision(1); // TypeError: '1.2'.toPrecision не є функцією
Створює екземпляр, що представляє помилку, яка виникає, коли encodeURI()
або decodeURI()
передають недійсні параметри.
decodeURI('%'); // URIError: неправильний URI
Початківці розробники JavaScript іноді просто кидають необроблені рядки, наприклад.
try {
throw 'Something bad happened';
}
catch(e) {
console.log(e);
}
Don't do that. Фундаментальна перевага об’єктів Error
полягає в тому, що вони автоматично відстежують, де вони були створені та що іх викликало за допомогою властивості stack
.
Необроблені рядки призводять до дуже болісного досвіду налагодження та ускладнюють аналіз помилок із журналів.
Передавати об’єкт Error
можна. Це традиційно для коду стилю зворотного виклику Node.js, який приймає зворотні виклики з першим аргументом як об’єкт помилки.
function myFunction (callback: (e?: Error)) {
doSomethingAsync(function () {
if (somethingWrong) {
callback(new Error('This is my error'))
} else {
callback();
}
});
}
Exceptions should be exceptional
(Винятки мають бути винятковими) — загальний вислів у інформатиці. Є кілька причин, чому це також стосується JavaScript (і TypeScript).
Розглянемо такий фрагмент коду:
try {
const foo = runTask1();
const bar = runTask2();
}
catch(e) {
console.log('Error:', e);
}
Наступний розробник не може знати, яка функція може викликати помилку. Людина, яка переглядає код, не може знати, не прочитавши код для task1 / task2 та інших функцій, які вони можуть викликати тощо.
Ви можете спробувати зробити це витонченим, чітко зафіксувавши кожну річ, яка може викинути:
try {
const foo = runTask1();
}
catch(e) {
console.log('Error:', e);
}
try {
const bar = runTask2();
}
catch(e) {
console.log('Error:', e);
}
Але тепер, якщо вам потрібно передати матеріали з першого завдання до другого, код стає безладним: (зверніть увагу на мутацію foo
, що вимагає let
+ явну потребу анотувати це, оскільки це не можна вивести з повернення runTask1
) :
let foo: number; // Notice use of `let` and explicit type annotation
try {
foo = runTask1();
}
catch(e) {
console.log('Error:', e);
}
try {
const bar = runTask2(foo);
}
catch(e) {
console.log('Error:', e);
}
Розглянемо функцію:
function validate(value: number) {
if (value < 0 || value > 100) throw new Error('Invalid value');
}
Використання Error
для таких випадків є поганою ідеєю, оскільки воно не представлено у визначенні типу для функції перевірки (це (value:number) => void
). Натомість кращим способом створення методу перевірки буде:
function validate(value: number): {error?: string} {
if (value < 0 || value > 100) return {error:'Invalid value'};
}
А тепер його представлено в системі типів.
Якщо ви не хочете обробити помилку дуже загальним (простим/всеохоплюючим тощо), не генеруйте помилку.