JavaRush /Курсы /Модуль 3: React /Типизация запросов и ответов в fetch с TypeScript

Типизация запросов и ответов в fetch с TypeScript

Модуль 3: React
11 уровень , 2 лекция
Открыта

Интерфейсы для типизации данных

Когда мы работаем с API, у нас есть два основных типа данных:

  1. Данные, которые отправляются.
  2. Данные, которые возвращаются.

TypeScript позволяет создавать интерфейсы для описания структуры этих данных.

Пример: интерфейсы для данных

Предположим, у нас есть REST API, который возвращает следующую структуру данных для пользователей при запросе GET /users:

{
  "id": 1,
  "name": "Alice",
  "email": "alice@example.com",
  "isAdmin": false
}

Мы можем описать эту структуру с помощью интерфейса:

interface User {
  id: number;
  name: string;
  email: string;
  isAdmin: boolean;
}

Для списка пользователей (возврат массива объектов) создадим тип:

type UsersResponse = User[];

Теперь TypeScript будет контролировать, чтобы любая операция с данными соответствовала этим типам.

Типизация запросов с fetch

Теперь вспомним базовый запрос с fetch и добавим типизацию.

Пример 1: типизация для GET-запроса

Напишем функцию, которая получает список пользователей:

Код без типизации:

async function fetchUsers() {
  const response = await fetch('https://api.example.com/users');
  const data = await response.json();
  return data;
}

С типизацией:

async function fetchUsers(): Promise<UsersResponse> {
  const response = await fetch('https://api.example.com/users');
  if (!response.ok) {
    throw new Error(`HTTP error! Status: ${response.status}`);
  }
  const data: UsersResponse = await response.json();
  return data;
}

Что здесь поменялось:

  1. Мы добавили возвращаемый тип Promise<UsersResponse> — функция возвращает промис, который в будущем разрешится массивом пользователей.
  2. Тип пользователей UsersResponse применяется при парсинге JSON (const data: UsersResponse = ...).

Теперь, если данные на сервере изменятся (например, будет добавлено поле или изменён тип), TypeScript сразу сообщит об этом.

Пример 2: типизация для POST-запроса

Предположим, нам нужно добавить нового пользователя через API, отправив его данные с запросом POST.

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

Функция для отправки POST-запроса:

async function createUser(user: NewUser): Promise<User> {
  const response = await fetch('https://api.example.com/users', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(user)
  });

  if (!response.ok) {
    throw new Error(`HTTP error! Status: ${response.status}`);
  }

  const createdUser: User = await response.json();
  return createdUser;
}

Что мы сделали:

  1. Создали отдельный интерфейс NewUser для данных, отправляемых в запросе.
  2. Указали возвращаемый тип как Promise<User> для данных, которые мы получим от сервера.
  3. Явно типизировали тело ответа сервера (const createdUser: User).

Теперь вызовы createUser требуют точного соответствия структур данных, что предотвращает ошибки.

Улучшение читаемости и повторного использования

Для более читаемого и повторного использования кода можно написать универсальную функцию запроса, которая принимает URL, метод и данные.

async function apiRequest<TRequest, TResponse>(
  url: string,
  method: 'GET' | 'POST' | 'PUT' | 'DELETE',
  body?: TRequest
): Promise<TResponse> {
  const response = await fetch(url, {
    method,
    headers: {
      'Content-Type': 'application/json'
    },
    body: body ? JSON.stringify(body) : undefined
  });

  if (!response.ok) {
    throw new Error(`HTTP error! Status: ${response.status}`);
  }

  const data: TResponse = await response.json();
  return data;
}

Использование:

const newUser: NewUser = { name: 'Bob', email: 'bob@example.com', password: '123456' };
const createdUser = await apiRequest<NewUser, User>(
  'https://api.example.com/users',
  'POST',
  newUser
);
console.log(createdUser.name);

Такой подход делает функцию универсальной — она может работать с любым запросом при условии указания типов.

Тип Response в TypeScript

JavaScript имеет встроенный объект Response для работы с HTTP-ответами. TypeScript предоставляет для него тип, который можно использовать.

Пример проверки статуса ответа:

async function fetchData(url: string): Promise<string> {
  const response: Response = await fetch(url);

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`Error: ${response.statusText} - ${errorText}`);
  }

  return response.text();
}

Типичные ошибки и как их избежать

Работая с типизацией запросов, важно помнить несколько моментов:

  1. Несоответствие типов. Если сервер возвращает данные, которые не соответствуют вашим интерфейсам, при отсутствии строгой проверки может возникнуть баг. Например, сервер добавил новое обязательное поле, или изменил тип данных — TypeScript здесь ваш главный защитник.

  2. Нет обработки ошибок. Не пропускайте проверку статуса ответа response.ok. Запрос может вернуть 404, 500, или другой HTTP-код, даже если он не вызывает явную ошибку в fetch.

  3. Неверная типизация тела запроса. Убедитесь, что данные, отправляемые в body, соответствуют интерфейсу. В противном случае сервер просто может отклонить запрос.

1
Задача
Модуль 3: React, 11 уровень, 2 лекция
Недоступна
Типизация ответа API
Типизация ответа API
1
Задача
Модуль 3: React, 11 уровень, 2 лекция
Недоступна
Типизация отправляемых данных
Типизация отправляемых данных
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ