Обработка ошибок в TypeScript
Дальше: Паттерны · TypeScript и Node.js · Асинхронность · Справочник — распространённые паттерны
Ошибки, исключения и отказоустойчивость — ошибка и исключение.
Синтаксис throw и try/catch в рантайме — Обработка исключений в JavaScript.
Механика throw в TypeScript та же, что в JavaScript. Зато типы помогают описать ошибки явно: ожидаемые сбои удобно класть в Result (discriminated union успех/ошибка), инфраструктурные — обрабатывать в catch с unknown. Так важные ветки обработки видны в контракте, без потери в общем try/catch. Удобный порядок — асинхронность, эта статья, паттерны, TypeScript и Node.js.
unknownв catch — Типы данных и типизация в TypeScript, Асинхронное программирование в TypeScript. Валидация — Рекомендации по разработке на TypeScript.
Два семейства ошибок
| Тип | Примеры | Как моделировать |
|---|---|---|
| Ожидаемые | невалидный email, ресурс не найден | Result, union, HTTP 4xx |
| Неожиданные | диск, OOM, баг | throw, HTTP 5xx, лог + alert |
// Ожидаемая — часть контракта
function parseAge(input: string): Result<number, "nan" | "negative"> { /* ... */ }
// Неожиданная — прерывает поток
async function readSecret(): Promise<string> {
const data = await fs.readFile("/secret", "utf8");
return data;
}
Result<T, E>
Код ITЗагрузка примера кода…
Разбор:
- Вызывающий обязан проверить
ok— иначе нет доступа кvalue. Eможно сузить до union литералов — автодополнение вif (!r.ok).
Комбинирование Result
function mapResult<T, U, E>(
r: Result<T, E>,
fn: (v: T) => U,
): Result<U, E> {
return r.ok ? { ok: true, value: fn(r.value) } : r;
}
В библиотеках: neverthrow, ts-results — по желанию команды.
Option<T> (отсутствие значения)
Код ITЗагрузка примера кода…
Разбор:
- Альтернатива —
T | nullсstrictNullChecks— Переменные и константы в TypeScript. Optionявно показывает в API, что значения может не быть.
try/catch и unknown
Код ITЗагрузка примера кода…
Разбор:
- Не используйте
catch (e: any). instanceof Errorне покрывает всеthrowв JS.
Кастомные классы ошибок
class AppError extends Error {
constructor(
message: string,
readonly code: "NOT_FOUND" | "FORBIDDEN" | "VALIDATION",
) {
super(message);
this.name = "AppError";
}
}
function isAppError(e: unknown): e is AppError {
return e instanceof AppError;
}
HTTP-маппинг в Node:
function toStatus(e: AppError): number {
switch (e.code) {
case "NOT_FOUND":
return 404;
case "FORBIDDEN":
return 403;
case "VALIDATION":
return 400;
}
}
Разбор:
- Класс удобен для центрального error middleware.
- Для чистых функций предпочтительнее
Resultбез throw.
Ошибки в async-цепочках
async function fetchUser(id: string): Promise<Result<User, "http" | "invalid">> {
try {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) return { ok: false, error: "http" };
const raw: unknown = await res.json();
if (!isUser(raw)) return { ok: false, error: "invalid" };
return { ok: true, value: raw };
} catch {
return { ok: false, error: "http" };
}
}
Promise reject и возврат Result — выберите один стиль на слой; смешение усложняет код.
Единый формат API-ошибки
Код ITЗагрузка примера кода…
Разбор:
- Frontend типизирует
ApiErrorBodyи показываетdetailsу полей формы.
Zod на границе
Код ITЗагрузка примера кода…
Подробнее — Рекомендации по разработке на TypeScript.
throw и Result — шпаргалка
| Ситуация | Рекомендация |
|---|---|
| Валидация формы / DTO | Result или Zod safeParse |
| Ресурс не найден в сервисе | Result или AppError NOT_FOUND |
| Падение диска / сеть без обработки | throw + middleware |
| React load data | union LoadState — TypeScript и React |
Частые ошибки
| Ошибка | Что делать |
|---|---|
catch (e: any) | unknown + narrowing |
Игнорировать !r.ok | включить ESLint на floating promises / strict |
| throw для валидации везде | Result на границе domain |
Один Error на всё | union кодов или классы |
| Типы ошибок не в API | ApiErrorBody |
Практика
- Перепишите парсер числа на
Result<number, "nan" | "negative">. - Добавьте
findByIdсOptionилиResult. - Сделайте
AppError+ маппинг в HTTP-статус. - В React отобразите
LoadStateerror — TypeScript и React. - Оберните
POSTbody в ZodsafeParse(опционально).