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

Типизация пропсов с интерфейсами и PropTypes

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

Почему типизация пропсов важна?

Пропсы — это основной способ передачи данных в React-компоненты. Они позволяют различным компонентам общаться друг с другом. Однако, если мы не будем типизировать пропсы, мы можем легко столкнуться с массой проблем:

  • Ошибки из-за несоответствия типов. Например, вместо объекта мы передадим строку — и приложение "ломается".
  • Трудности при сопровождении кода. С ростом проекта отсутствие типизации делает код менее читаемым и понятным.
  • Нет автодополнения в IDE. Без типизации разработчики теряют мощные инструменты подсказок.

К счастью, в React у нас есть два мощных инструмента для работы с типами пропсов: интерфейсы TypeScript и PropTypes. Давайте их разбирать.

Типизация пропсов с интерфейсами TypeScript

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

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

Пример простого компонента с типизацией пропсов:

import React from 'react';

// Создаем интерфейс для пропсов
interface GreetingProps {
  name: string; // Ожидаем строку
  age?: number; // Возраст опциональный, знак "?" говорит об этом
}

// Используем интерфейс в компоненте
const Greeting: React.FC<GreetingProps> = ({ name, age }) => {
  return (
    <div>
      <h1>Привет, {name}!</h1>
      {age && <p>Тебе {age} лет.</p>}
    </div>
  );
};

// Используем компонент
export const App = () => {
    <Greeting name="Иван" age={25} />;
}

Что здесь происходит:

  1. Мы создали интерфейс GreetingProps, который описывает ожидаемые пропсы.
  2. Мы явно указали React.FC<GreetingProps> для компонента, чтобы TypeScript мог проверить соответствие переданных данных интерфейсу.
  3. Проп age помечен как опциональный. Если его не передать — всё будет работать.

Передача массивов, объектов и сложных типов

Что если наш компонент ожидает более сложные типы данных, такие как массивы или объекты? TypeScript справляется с этим на ура:

interface TodoItem {
  id: number;
  title: string;
  completed: boolean;
}

interface TodoListProps {
  items: TodoItem[]; // Массив объектов типа TodoItem
}

const TodoList: React.FC<TodoListProps> = ({ items }) => {
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>
          {item.title} {item.completed ? "✅" : "❌"}
        </li>
      ))}
    </ul>
  );
};

// Пример данных и использования
const todos = [
  { id: 1, title: "Написать лекцию по TypeScript", completed: true },
  { id: 2, title: "Сделать рефакторинг кода", completed: false },
];

export const App = () => <TodoList items={todos} />;
  • Здесь мы описали сложный объект TodoItem в интерфейсе и указали, что компонент получает массив таких объектов.
  • TypeScript автоматически проверит структуру каждого переданного объекта.

Необязательные и значения по умолчанию

Иногда пропсы могут отсутствовать, и в таком случае уместно задавать значения по умолчанию через defaultProps или с помощью ES6-деструкции.

interface GreetingProps {
  name: string;
  age?: number; // Возраст опционален
}

const Greeting: React.FC<GreetingProps> = ({ name, age = 18 }) => {
  return (
    <div>
      <h1>Привет, {name}!</h1>
      <p>Тебе {age} лет.</p>
    </div>
  );
};

// Работает даже без указания "age"
export const App = () => <Greeting name="Мария" />;

PropTypes для валидации данных

TypeScript отлично работает на этапе разработки, но в реальной жизни (в Production) он уже не спасет от ошибок: данные могут прийти неверные. В таких случаях на помощь приходит встроенная библиотека PropTypes.

Валидация пропсов с PropTypes

PropTypes проверяет данные во время выполнения. Вот пример использования:

import PropTypes from 'prop-types';

const Button = ({ label, onClick }) => {
  return <button onClick={onClick}>{label}</button>;
};

// Определяем PropTypes
Button.propTypes = {
  label: PropTypes.string.isRequired, // Обязательно строка
  onClick: PropTypes.func.isRequired, // Обязательно функция
};

// Используем компонент
export const App = () => (
  <Button label="Нажми меня" onClick={() => console.log("Клик!")} />
);

Что здесь происходит:

  1. Мы объявляем ожидаемые типы данных для пропсов прямо в поле propTypes.
  2. Пропс label должен быть строкой и обязательно передан isRequired.
  3. Если тип пропса или его наличие не соответствует указанным правилам, в консоли выведется предупреждение.

Поддержка сложных типов и массивов

В PropTypes можно описывать массивы, объекты и их структуры:

const TodoList = ({ todos }) => {
  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>
          {todo.text} {todo.completed ? "✅" : "❌"}
        </li>
      ))}
    </ul>
  );
};

TodoList.propTypes = {
  todos: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.number.isRequired,
      text: PropTypes.string.isRequired,
      completed: PropTypes.bool.isRequired,
    })
  ).isRequired,
};

// Пример использования
export const App = () => (
  <TodoList
          todos={[
          { id: 1, text: "Выучить TypeScript", completed: true },
      { id: 2, text: "Сделать домашку", completed: false },
    ]}
  />
);
  • arrayOf проверяет каждый элемент массива.
  • shape задает форму объектов, переданных в массиве.

Сравнение TypeScript и PropTypes

Возможно, у вас возник вопрос: "Зачем использовать PropTypes, если есть TypeScript?" Всё зависит от контекста:

Особенность TypeScript PropTypes
Когда проверяет На этапе разработки (compile time) Во время выполнения (runtime)
Типы данных Более мощные, включает union, generics и т.д. Ограниченные (строка, число, и т.д.)
Производство (Prod) Работает только в разработке Проверяет в runtime, работает в Prod
Легкость изучения Требует знания TypeScript Прост в освоении

В идеале, TypeScript и PropTypes можно использовать вместе. TypeScript заботится о разработке, а PropTypes защищает от реальных ошибок в продакшене.

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

  1. Отсутствие типизации сложных объектов. Никогда не оставляйте массивы или объекты "неявными". Сделайте интерфейсы для них с TypeScript или используйте PropTypes.shape.

  2. Пропущенные опциональные пропсы. Убедитесь, что опциональные пропсы корректно помечены (? в TypeScript или без isRequired в PropTypes). Это избавит от редких багов.

  3. Конфликт между defaultProps и TypeScript. Если используете defaultProps, убедитесь, что TypeScript не считает эти пропсы обязательными. В TypeScript 4.0+ рекомендуют использовать ES6-деструкцию вместо defaultProps.

Применение на практике

Теперь, когда у вас есть знания о TypeScript и PropTypes, вы можете защитить свои компоненты как на этапе разработки, так во время работы. Это крайне важно в крупных коммерческих проектах, где ошибки и баги стоят времени и денег. Используйте TypeScript для типизации компонентов, а PropTypes для runtime-проверок.

И помните: пропсы — это как границы в отношениях. Лучше сразу договориться, что кому можно передавать, чтобы потом не возникло конфликтов!

1
Задача
Модуль 3: React, 1 уровень, 7 лекция
Недоступна
Создание компонента с использованием интерфейса для пропсов
Создание компонента с использованием интерфейса для пропсов
1
Задача
Модуль 3: React, 1 уровень, 7 лекция
Недоступна
Валидация пропсов с PropTypes
Валидация пропсов с PropTypes
Комментарии (2)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Константин Уровень 68
21 октября 2025
PropTypes больше не встроена в React, теперь устанавливается отдельно
Александр Уровень 90
27 августа 2025
ES6-деструкция - это так же круто, как и деструктуризация, Вась