TypeScript: типизация API-ответа с дженериками
Правильная типизация ответов от API с использованием дженериков. Создание переиспользуемого типа ApiResponse<T> для работы с различными эндпоинтами.
Типизация ответов от API — одна из самых частых задач в TypeScript. Дженерики позволяют создать переиспользуемый тип для любых данных.
Базовый тип ApiResponse
interface ApiResponse<T> {
data: T;
status: number;
message?: string;
}
Типизация конкретного эндпоинта
// Тип для пользователя
interface User {
id: number;
name: string;
email: string;
}
// Функция с типизацией
async function fetchUser(id: number): Promise<ApiResponse<User>> {
const response = await fetch(`/api/users/${id}`);
const result: ApiResponse<User> = await response.json();
return result;
}
// Использование
const userData = await fetchUser(1);
console.log(userData.data.name); // TypeScript знает структуру
console.log(userData.data.email); // Автодополнение работает
Типизация списка элементов
interface Product {
id: number;
name: string;
price: number;
}
async function fetchProducts(): Promise<ApiResponse<Product[]>> {
const response = await fetch('/api/products');
return await response.json();
}
const productsData = await fetchProducts();
productsData.data.forEach(product => {
console.log(product.name, product.price); // Типизация работает
});
Обработка ошибок
interface ApiError {
code: string;
message: string;
}
type ApiResult<T> = ApiResponse<T> | { error: ApiError };
async function safeFetchUser(id: number): Promise<ApiResult<User>> {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
return { error: { code: 'FETCH_ERROR', message: response.statusText } };
}
return await response.json();
} catch (error) {
return { error: { code: 'NETWORK_ERROR', message: 'Network request failed' } };
}
}
// Использование с проверкой типа
const result = await safeFetchUser(1);
if ('error' in result) {
console.error(result.error.message);
} else {
console.log(result.data.name);
}
Расширенный вариант с метаданными
interface PaginationMeta {
page: number;
perPage: number;
total: number;
totalPages: number;
}
interface ApiResponseWithPagination<T> {
data: T;
meta: PaginationMeta;
status: number;
}
async function fetchUsersPage(page: number): Promise<ApiResponseWithPagination<User[]>> {
const response = await fetch(`/api/users?page=${page}`);
return await response.json();
}
const pageData = await fetchUsersPage(1);
console.log(pageData.meta.total); // Типизация метаданных
console.log(pageData.data.length); // Типизация данных
Usage:
Используй ApiResponse<T> для всех API-вызовов. Дженерик T позволяет типизировать разные структуры данных без дублирования кода.
Notes:
⚠️ Всегда проверяй наличие data перед использованием. Используй type guards для безопасной работы с union types. Дженерики делают код переиспользуемым и типобезопасным одновременно.