← Назад к вопросам

Как типизировали запросы?

1.8 Middle🔥 161 комментариев
#Браузер и сетевые технологии

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Типизация запросов в современном Frontend-разработке

Типизация запросов (чаще всего HTTP-запросов к API) — это критически важный аспект разработки надежных и поддерживаемых фронтенд-приложений. Моя практика включает несколько подходов, которые эволюционировали вместе с развитием инструментов.

Основные стратегии типизации

1. Явная типизация с использованием TypeScript Interfaces/Type Aliases Это фундаментальный подход. Для каждого endpoint API создается интерфейс, точно описывающий структуру ожидаемого ответа и, если необходимо, тела запроса.

// Типизация для эндпоинта получения пользователя
interface UserResponse {
  id: number;
  name: string;
  email: string;
  createdAt: string; // ISO date string
}

interface CreateUserRequest {
  name: string;
  email: string;
  password: string;
}

// Пример использования в функции запроса
async function fetchUser(userId: number): Promise<UserResponse> {
  const response = await fetch(`/api/users/${userId}`);
  const data: UserResponse = await response.json();
  return data;
}

2. Генерация типов из спецификаций (OpenAPI/Swagger) Для крупных проектов с задокументированным API использую инструменты автоматической генерации. Это обеспечивает полную консистентность между фронтендом и бэкендом.

# Пример использования openapi-typescript
npx openapi-typescript https://api.example.com/openapi.json --output ./src/types/api.ts

Генерированный файл содержит все интерфейсы для запросов и ответов, которые можно импортировать непосредственно в код.

3. Типизация с помощью библиотек HTTP-клиентов При использовании axios или fetch с обертками создаю универсальные типизированные клиенты.

// Типизированный клиент с axios
import axios, { AxiosInstance, AxiosResponse } from 'axios';

class ApiClient {
  private client: AxiosInstance;

  constructor(baseURL: string) {
    this.client = axios.create({ baseURL });
  }

  // Метод с полной типизацией входа и выхода
  public async get<T>(url: string): Promise<AxiosResponse<T>> {
    return this.client.get<T>(url);
  }

  public async post<T, R>(url: string, data: T): Promise<AxiosResponse<R>> {
    return this.client.post<R>(url, data);
  }
}

// Использование
const api = new ApiClient('https://api.example.com');
const userResponse = await api.get<UserResponse>('/users/1');

Практические шаги и лучшие практики

В реальных проектах я комбинирую следующие техники:

  • Создание централизованного модуля типов API: Все интерфейсы для запросов/ответов хранятся в src/types/api.ts или аналогичном файле.

  • Типизация ошибок: Не только успешные ответы, но и структуры ошибок должны быть типизированы.

    interface ApiErrorResponse {
      code: string;
      message: string;
      details?: Record<string, unknown>;
    }
    
  • Использование Generic Types в функциях-помощниках: Создание универсальных функций для обработки запросов.

    async function safeFetch<T>(url: string, options?: RequestInit): Promise<T> {
      const response = await fetch(url, options);
      if (!response.ok) {
        throw new Error(`HTTP error ${response.status}`);
      }
      return response.json() as T;
    }
    
  • Интеграция с состояниями (React/Vue): При использовании стейт-менеджеров (React Query, Vuex, Pinia) типы запросов напрямую связываются с типами состояния.

    // Пример с React Query и TypeScript
    const { data } = useQuery<UserResponse, ApiErrorResponse>({
      queryKey: ['user', userId],
      queryFn: () => fetchUser(userId),
    });
    
  • Валидация ответов на соответствие типам: В сложных сценариях использую библиотеки валидации (например, zod или yup) вместе с TypeScript для runtime-проверки.

    import { z } from 'zod';
    
    const UserSchema = z.object({
      id: z.number(),
      name: z.string().min(1),
    });
    
    type UserResponse = z.infer<UserSchema>; // Тип генерируется из схемы!
    
    const data = await fetchUser(userId);
    const validatedUser = UserSchema.parse(data); // Runtime валидация
    

Преимущества глубокой типизации запросов

  • Безопасность типов: Компилятор TypeScript предупреждает о несоответствии данных.
  • Улучшенная документация: Интерфейсы служат как документация форматов данных.
  • Более быстрая разработка: Автозаполнение и IntelliSense в IDE значительно сокращают время.
  • Сокращение ошибок runtime: Типизация предотвращает множество ошибок, связанных с неправильным предположением о структуре данных.
  • Легкая рефакторинг: При изменении API контракта ошибки сразу проявляются на этапе компиляции.

Таким образом, типизация запросов — это не просто добавление interface, это системный подход, который включает генерацию типов, создание типизированных клиентов, валидацию и интеграцию со всем стейт-менеджментом приложения. Это существенно повышает надежность и снижает количество дефектов в production.