Интерфейсы для типизации данных
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".
Интерфейсы для задач:
interface Todo { id: number; title: string; completed: boolean; } type TodosList = Todo[];Создание функции для получения задач:
const fetchTodos = async (): Promise<TodosList> => { const response = await fetch('https://jsonplaceholder.typicode.com/todos'); if (!response.ok) { throw new Error('Ошибка при загрузке задач'); } return response.json(); };Использование данных в компоненте:
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 делают нашу жизнь проще.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ