TYPESCRIPT
#typescript#api#generics#typing#interfaces

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. Дженерики делают код переиспользуемым и типобезопасным одновременно.