TypeScript
Типобезопасный клиент API
Дженерик-обёртка над 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);
}