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

Типизация данных для статических страниц с TypeScript

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

Типизация getStaticProps

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

// pages/blog.tsx
export const getStaticProps = async () => {
    const response = await fetch("https://jsonplaceholder.typicode.com/posts");
    const posts = await response.json();

    return {
        props: {
            posts, // Здесь пока нет типизации
        },
    };
};

const Blog = ({ posts }: { posts: any[] }) => {
    return (
        <div>
            <h1>Блог</h1>
            {posts.map((post) => (
                <div key={post.id}>
                    <h2>{post.title}</h2>
                    <p>{post.body}</p>
                </div>
            ))}
        </div>
    );
};

export default Blog;

Неплохо, но есть проблемы:

  1. Мы используем any для данных. Это открывает дверь ошибкам.
  2. Нет гарантии, что у каждого поста будут поля title и body.

Теперь добавим типизацию:

// Определяем интерфейс для данных поста
interface Post {
    id: number;
    title: string;
    body: string;
}

// Типизируем результат getStaticProps
import { GetStaticProps } from "next";

export const getStaticProps: GetStaticProps<{ posts: Post[] }> = async () => {
    const response = await fetch("https://jsonplaceholder.typicode.com/posts");
    const posts: Post[] = await response.json();

    return {
        props: {
            posts,
        },
    };
};

const Blog = ({ posts }: { posts: Post[] }) => {
    return (
        <div>
            <h1>Блог</h1>
            {posts.map((post) => (
                <div key={post.id}>
                    <h2>{post.title}</h2>
                    <p>{post.body}</p>
                </div>
            ))}
        </div>
    );
};

export default Blog;

Теперь TypeScript защитит нас от:

  • Ошибочного использования полей, которых нет в API-ответе.
  • Отсутствия обязательных данных в массиве posts.

Типизация через интерфейсы — немного глубже

Что делать, если данные сложные? Например, каждый пост может содержать автора с именем и email. Мы можем вложить интерфейсы друг в друга:

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

interface Post {
    id: number;
    title: string;
    body: string;
    author: Author;
}

И теперь, если в API ответа не будет поля author.name, компилятор быстро нас об этом предупредит.

Типизация ответа API

Когда вы работаете с внешним API, важно, чтобы и его структура была типизирована. Иногда API может возвращать дополнительные поля, которые вам не нужны, или JSON может быть оформлен не так, как вы ожидали. Вот пример, как это можно обработать:

// Убедимся, что данные от API соответствуют нашему интерфейсу
const fetchPosts = async (): Promise<Post[]> > {
    const response = await fetch("https://jsonplaceholder.typicode.com/posts");
    const data = await response.json();

    // Для простоты не будем сейчас валидировать поля, но можно использовать библиотеки, такие как Yup или Zod.
    return data.map((item: any) => ({
        id: item.id,
        title: item.title,
        body: item.body,
        author: {
            name: item.userId, // Здесь, например, ошибка, потому что userId не строка!
            email: "example@example.com", // Заглушка для демонстрации
        },
    }));
};

Типизируем компоненты

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

// Компонент для отображения одного поста
const PostCard: React.FC<{ post: Post }> = ({ post }) => {
    return (
        <div>
            <h2>{post.title}</h2>
            <p>{post.body}</p>
            <small>Автор: {post.author.name}</small>
        </div>
    );
};

// Используем PostCard внутри основного компонента
const Blog: React.FC<{ posts: Post[] }> = ({ posts }) => {
    return (
        <div>
            <h1>Блог</h1>
            {posts.map((post) => (
                <PostCard key={post.id} post={post} />
            ))}
        </div>
    );
};

Работа с API-ошибками

Иногда API может возвращать ошибочные данные или вовсе не работать. Вот пример, как это обработать:

interface Post {
    id: number;
    title: string;
    body: string;
}

interface ErrorProps {
    message: string;
}

export const getStaticProps: GetStaticProps<{
    posts?: Post[];
    error?: ErrorProps;
}> = async () => {
    try {
        const response = await fetch("https://jsonplaceholder.typicode.com/posts");
        if (!response.ok) {
            throw new Error(`Failed to fetch: ${response.statusText}`);
        }
        const posts: Post[] = await response.json();
        return {
            props: { posts },
        };
    } catch (error) {
        return {
            props: {
                error: { message: error.message },
            },
        };
    }
};

const Blog = ({ posts, error }: { posts?: Post[]; error?: ErrorProps }) => {
    if (error) {
        return <p>Ошибка: {error.message}</p>;
    }

    return (
        <div>
            <h1>Блог</h1>
            {posts?.map((post) => (
                <div key={post.id}>
                    <h2>{post.title}</h2>
                    <p>{post.body}</p>
                </div>
            ))}
        </div>
    );
};

export default Blog;

Стратегии для работы с типизацией API

Если ваш API-ответ сильно варьируется, есть смысл использовать схемы валидации данных, такие как Yup или Zod. Эти библиотеки позволяют описывать данные декларативно и проверять их перед использованием. Например:

import * as yup from "yup";

const PostSchema = yup.object({
    id: yup.number().required(),
    title: yup.string().required(),
    body: yup.string().required(),
});

// Применяем схему при получении данных
const validatePost = (data: any): Post => {
    return PostSchema.validateSync(data) as Post;
};

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

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