Интерфейсы для запросов и ответов
Для работы с TypeScript мы будем использовать интерфейсы — любимый инструмент для описания формата наших данных.
Пример интерфейса
Предположим, у нас есть API, которое получает список пользователей. Оно возвращает массив объектов с информацией о пользователях: ID, имя и email. Вот как мы можем описать эти данные:
// Описание структуры данных, возвращаемых API
interface User {
id: number;
name: string;
email: string;
}
Если вы когда-либо работали с JSON, то интерфейсы — это просто его "тип". Зная структуру данных, которые вы ожидаете, вы можете заранее описать их формат.
Типизация данных при использовании Axios
Когда мы делаем запросы с Axios, мы можем явно указать, какого типа ответ мы ожидаем. Это особенно полезно, если API возвращает сложные структуры.
Основной синтаксис типизации Axios
Axios предоставляет возможность определить тип ответа, используя параметр <T>.
Вот как это работает:
import axios from 'axios';
// Типизируем наши данные
interface User {
id: number;
name: string;
email: string;
}
// Делаем запрос и типизируем ответ
axios.get<User[]>('https://jsonplaceholder.typicode.com/users')
.then(response => {
// Теперь TypeScript знает, что response.data — это массив объектов User
const users = response.data;
console.log(users[0].name); // Всё ок — TypeScript подскажет, что name есть у User
})
.catch(error => {
console.error('Ошибка при запросе:', error);
});
В данном примере:
User[]говорит, что ответ будет массивом объектов типаUser.- TypeScript будет ругаться, если вы попробуете получить, например,
users[0].age(ведь мы не указали, что уUserесть полеage).
Подключение типов к конфигурации запросов
Иногда запросы требуют дополнительных данных: параметров, заголовков или тела. Хорошая новость: мы тоже можем их типизировать.
Предположим, вы хотите добавить нового пользователя через API. Для этого вам нужно отправить POST-запрос с телом, содержащим данные пользователя. Мы можем заранее типизировать это тело:
interface NewUser {
name: string;
email: string;
password: string;
}
// Типизируем тело запроса
const newUser: NewUser = {
name: 'Иван Иванов',
email: 'ivan@example.com',
password: 'password123'
};
// Отправляем запрос
axios.post<User>('https://jsonplaceholder.typicode.com/users', newUser)
.then(response => {
// Ответ автоматически типизируется как User
console.log('Создан пользователь:', response.data);
})
.catch(error => {
console.error('Ошибка при создании пользователя:', error);
});
Преимущества точной типизации в Axios
Теперь, когда мы типизируем запросы, возникает вопрос: а что если ответ не соответствует ожидаемой структуре?
Встроенная защита от ошибок
TypeScript помогает нам избежать таких проблем ещё на этапе разработки. Например, если вы добавите поле, которого нет в интерфейсе, TypeScript сразу даст вам знать.
// Ошибка! Поле "age" не определено в интерфейсе NewUser
const invalidUser: NewUser = {
name: 'Иван',
email: 'ivan@example.com',
password: '123456',
age: 30 // <- TypeScript скажет: "Эй, этого поля нет!"
};
Продвинутый пример: обработка ответа с дополнительной метаинформацией
Иногда сервер возвращает не только данные, но и дополнительную информацию, такую как общее количество записей, пагинация и т.д. Как типизировать такие ответы?
Пример с пагинацией
Допустим, наш API возвращает такой ответ:
{
"data": [
{ "id": 1, "name": "Иван Иванов", "email": "ivan@example.com" },
{ "id": 2, "name": "Мария Петрова", "email": "maria@example.com" }
],
"total": 50,
"page": 1,
"pageSize": 10
}
Мы можем описать тип такого ответа следующим образом:
interface PaginationResponse<T> {
data: T[];
total: number;
page: number;
pageSize: number;
}
// Типизируем данные пользователей
interface User {
id: number;
name: string;
email: string;
}
// Типизируем ответ API
axios.get<PaginationResponse<User>>('https://jsonplaceholder.typicode.com/users')
.then(response => {
const { data, total, page, pageSize } = response.data;
console.log(`Получено ${data.length} пользователей из ${total}`);
})
.catch(error => {
console.error('Ошибка при запросе:', error);
});
Теперь можно легко переиспользовать PaginationResponse для других типов данных, не только User.
Типизация ошибок
Типизация полезна не только для успешных запросов, но и для обработки ошибок.
Axios позволяет вам перехватывать ошибки и обрабатывать их централизованно:
interface ApiError {
message: string;
statusCode: number;
}
axios.get<User[]>('https://jsonplaceholder.typicode.com/404')
.then(response => {
console.log('Успешный ответ:', response.data);
})
.catch((error: Axios.AxiosError<ApiError>) => {
if (error.response) {
console.error(`Ошибка API: ${error.response.data.message}`);
} else {
console.error('Неизвестная ошибка:', error.message);
}
});
Реальный пример: создание API-сервиса
Часто бывает удобно вынести всю логику работы с API в отдельный модуль. Давайте создадим простой API-сервис для работы с пользователями.
import axios from 'axios';
interface User {
id: number;
name: string;
email: string;
}
const api = axios.create({
baseURL: 'https://jsonplaceholder.typicode.com'
});
export const getUsers = async (): Promise<User[]> => {
const response = await api.get<User[]>('/users');
return response.data;
};
export const createUser = async (user: Omit<User, 'id'>): Promise<User> => {
const response = await api.post<User>('/users', user);
return response.data;
};
// Пример использования
getUsers().then(users => console.log(users));
createUser({ name: 'Иван', email: 'ivan@example.com' }).then(user => console.log(user));
Вот так мы создали удобный сервис с функциями getUsers и createUser. Его можно легко использовать в любом компоненте React.
TypeScript и Axios — действительно мощная комбинация, которая позволяет разработчикам писать более безопасный и читаемый код. Теперь запросы к API становятся гораздо менее пугающими!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ