Проблема винятків у TypeScript: сигнатура функції не каже, які помилки вона може кинути. function parse(s: string): Config може кинути що завгодно, а в catch (error) змінна має тип unknown. Компілятор не змусить обробити помилку.
Тип Result робить помилку частиною повернутого значення:
type Result<T, E = Error> =
| { ok: true; value: T }
| { ok: false; error: E };
type AgeError = 'not-a-number' | 'negative' | 'too-large';
function parseAge(input: string): Result<number, AgeError> {
const n = Number(input);
if (Number.isNaN(n)) return { ok: false, error: 'not-a-number' };
if (n < 0) return { ok: false, error: 'negative' };
if (n > 150) return { ok: false, error: 'too-large' };
return { ok: true, value: n };
}
const result = parseAge(form.age);
if (!result.ok) {
showError(messages[result.error]); // error: AgeError - відомо, які бувають
return;
}
saveAge(result.value); // value: number - лише після перевірки
Що це дає:
- помилки видно в сигнатурі - і їх неможливо «забути»: до
valueне дістатися без перевіркиok; - точні типи помилок - перелік очікуваних випадків, а не
unknown; - вичерпна обробка:
switch (result.error)з перевіркоюneverгарантує, що кожен варіант оброблено; - легко тестувати: функція повертає дані, а не кидає.
Коли Result виправданий:
- очікувані бізнес-помилки: валідація, «товару немає на складі», «недостатньо коштів», відповіді API з відомими кодами;
- місця, де помилку треба обробити поруч з викликом.
Коли краще винятки:
- неочікувані збої (баги, недоступна мережа, порушені інваріанти) - їх обробляють на межі застосунку, а не в кожному виклику;
- код, що працює з фреймворками, які очікують винятків (межі помилок React, обробники помилок у Laravel-стилі).
Пастки:
- ланцюжки Result-ів многослівні (
if (!r.ok) return r;на кожному кроці). Бібліотеки (neverthrow, Effect) даютьmap,andThen- але додають новий стиль коду, який команда має прийняти; - змішування стилів: функція, що повертає Result, але всередині може кинути виняток, - найгірший варіант. Межа має бути чіткою: на якому рівні винятки перетворюються на Result.