TypeScript

Типобезопасный клиент API

Algonexys · 07.08.2026 · 👁 0

Дженерик-обёртка над fetch с типами ответа, дискриминированным объединением для ошибок и без any.

Код

type ApiResult<T> =
  | { ok: true; data: T }
  | { ok: false; error: string; status?: number };

interface RequestOptions extends RequestInit {
  timeout?: number;
}

export async function apiRequest<T>(
  url: string,
  { timeout = 10_000, ...init }: RequestOptions = {},
): Promise<ApiResult<T>> {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeout);

  try {
    const response = await fetch(url, {
      ...init,
      signal: controller.signal,
      headers: { 'Content-Type': 'application/json', ...init.headers },
    });

    if (!response.ok) {
      return { ok: false, error: response.statusText, status: response.status };
    }
    return { ok: true, data: (await response.json()) as T };
  } catch (error) {
    const message = error instanceof Error ? error.message : 'Неизвестная ошибка';
    return { ok: false, error: message };
  } finally {
    clearTimeout(timer);
  }
}

interface User { id: number; username: string; email: string }

const result = await apiRequest<User[]>('/api/users/');
if (result.ok) {
  result.data.forEach((user) => console.log(user.username));  // data типизирована
} else {
  console.error(result.error);
}