JavaRush /Курсы /Модуль 3: React /Типизация запросов и ответов API с TypeScript — интерфейс...

Типизация запросов и ответов API с TypeScript — интерфейсы для данных

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

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

TypeScript позволяет описывать структуру объектов с помощью интерфейсов. Интерфейсы выступают как "контракты" данных: они говорят, как должна выглядеть структура объекта, с которым вы работаете.

Пример:

Представим, что у нас есть API, возвращающее информацию о пользователе в следующем виде:

{
  "id": 1,
  "name": "John Doe",
  "email": "john@example.com"
}

Для типизации таких данных создаём интерфейс в TypeScript:

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

Теперь при получении данных от API мы можем использовать этот интерфейс для проверки типа.

Типизация запросов к API

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

1. Интерфейсы для GET-запросов

Когда мы выполняем GET-запрос к API, данные, которые мы получаем, должны соответствовать определённой структуре. Например, для API "получить список пользователей":

[
  {
    "id": 1,
    "name": "John Doe",
    "email": "john@example.com"
  },
  {
    "id": 2,
    "name": "Jane Smith",
    "email": "jane@example.com"
  }
]

Создаём интерфейс для списка пользователей:

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

type UsersList = User[]; // Это массив объектов User

Теперь мы одним движением можем типизировать ответ:

const fetchUsers = async (): Promise<UsersList> => {
  const response = await fetch('https://api.example.com/users');
  if (!response.ok) {
    throw new Error('Ошибка при загрузке пользователей');
  }
  return response.json(); // TypeScript знает, что это UsersList
};

2. Интерфейсы для POST-запросов

POST-запросы часто отправляют на сервер данные. Например, вы хотите отправить информацию о новом пользователе:

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

Создаём функцию для отправки данных:

const createUser = async (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('Ошибка при создании пользователя');
  }

  return response.json(); // Ожидаем объект типа User
};

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

3. Интерфейсы для ошибок

Не забывайте про обработку ошибок. Часто API может возвращать сообщения об ошибках в подобном формате:

{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Invalid email address"
}

Мы добавим интерфейс для таких ситуаций:

interface ApiError {
  statusCode: number;
  error: string;
  message: string;
}

Это позволит обработать ошибки так:

try {
  const user = await createUser({ name: 'Invalid', email: 'oops.com' });
} catch (err) {
  if ((err as Response).status === 400) {
    const errorDetails: ApiError = await (err as Response).json();
    console.error(`Ошибка: ${errorDetails.message}`);
  }
}

Типизация ответов с вложенными структурами

Иногда ваши данные содержат вложенные объекты. Например:

{
  "id": 1,
  "title": "My post",
  "author": {
    "id": 2,
    "name": "Jane Smith"
  },
  "comments": [
    { "id": 101, "text": "Great post!", "authorId": 3 },
    { "id": 102, "text": "Thanks for sharing!", "authorId": 4 }
  ]
}

Для таких данных мы можем создать несколько интерфейсов:

interface Author {
  id: number;
  name: string;
}

interface Comment {
  id: number;
  text: string;
  authorId: number;
}

interface Post {
  id: number;
  title: string;
  author: Author;
  comments: Comment[];
}

Теперь ваш код будет выглядеть элегантно и предсказуемо:

const fetchPost = async (postId: number): Promise<Post> => {
  const response = await fetch(`https://api.example.com/posts/${postId}`);
  if (!response.ok) {
    throw new Error('Ошибка при загрузке поста');
  }
  return response.json(); // Возвращает строго типизированный объект Post
};

Типизация данных: практика

Давайте создадим небольшой пример API-запроса и подключим его к вашему приложению. Мы будем запрашивать список задач для приложения "To-Do List".

  1. Интерфейсы для задач:

    interface Todo {
    id: number;
    title: string;
    completed: boolean;
    }
    type TodosList = Todo[];
    
  2. Создание функции для получения задач:

    const fetchTodos = async (): Promise<TodosList> => {
    const response = await fetch('https://jsonplaceholder.typicode.com/todos');
    if (!response.ok) {
      throw new Error('Ошибка при загрузке задач');
    }
    return response.json();
    };
    
  3. Использование данных в компоненте:

    import React, { useEffect, useState } from 'react';
    
    const TodoList: React.FC = () => {
      const [todos, setTodos] = useState<TodosList>([]);
    
      useEffect(() => {
        (async () => {
          const fetchedTodos = await fetchTodos();
          setTodos(fetchedTodos.slice(0, 10)); // Отображаем только 10 задач
        })();
      }, []);
    
      return (
        <ul>
          {todos.map(todo => (
            <li key={todo.id}>
              {todo.title} {todo.completed && '✔️'}
            </li>
          ))}
        </ul>
      );
    };
    
    export default TodoList;

Теперь мы можем быть уверены, что данные, получаемые от API, строго соответствуют контракту, а приложение не упадёт из-за неожиданных структур. Гарантии TypeScript делают нашу жизнь проще.

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