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

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

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

Типизация компонентов в React

В React компоненты можно воспринимать как конструктор: вы собираете интерфейс из отдельных блоков. Пропсы, передаваемые в компонент, являются "инструкцией" для этого блока — какие атрибуты и данные он должен использовать. TypeScript помогает сделать эти "инструкции" строгими, а значит — надёжными. Давайте разберёмся, как это работает.

Что такое пропсы и зачем их типизировать?

Пропсы (props) — это данные, которые вы передаёте в компонент для его отображения или работы. Например:

function Greeting(props: { name: string }) {
  return <h1>Привет, {props.name}!</h1>;
}

Пропсы здесь — это объект, содержащий одно поле name с типом string. Типизация позволяет IDE (например, VS Code) сразу подсказать вам, что передать в компонент, а если вы ошиблись, она укажет на проблему ещё до запуска.

Почему это важно? Если вы передадите что-то странное, например, число вместо строки, TypeScript вас остановит! Без типизации ошибка всплывёт только в браузере, а до этого её ещё надо отловить.

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

Типы в TypeScript можно задавать разными способами. Но для React компонентов на практике чаще всего используются интерфейсы. Интерфейсы — это способ описать структуру объекта. Вот как это выглядит:

interface GreetingProps {
  name: string;
}

function Greeting(props: GreetingProps) {
  return <h1>Привет, {props.name}!</h1>;
}

// Использование компонента
<Greeting name="Иван" />;
  • interface GreetingProps описывает, какие именно пропсы принимает компонент Greeting.
  • Если вы попытаетесь передать что-то неподходящее, TypeScript сразу сообщит об ошибке.

Пример ошибки:

<Greeting name={42} />; // Ошибка: Type 'number' is not assignable to type 'string'.

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

Иногда пропсы могут быть необязательными. Например, если мы хотим добавить поле age, но оно не всегда нужно:

interface GreetingProps {
  name: string;
  age?: number; // ? обозначает, что пропс необязателен
}

function Greeting(props: GreetingProps) {
  return (
    <div>
      <h1>Привет, {props.name}!</h1>
      {props.age && <p>Тебе {props.age} лет.</p>}
    </div>
  );
}

// Использование компонента
<Greeting name="Иван" />;
<Greeting name="Иван" age={25} />;

Если age не передан, код внутри проверки props.age && просто не выполнится.

Значения по умолчанию для пропсов

Если вы хотите, чтобы пропсы имели значение по умолчанию, но при этом оставались необязательными, добавьте их значения в теле функции:

interface GreetingProps {
  name: string;
  age?: number;
}

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

// Даже если возраст не указан, мы покажем 18
<Greeting name="Иван" />;

Здесь age = 18 устанавливает значение по умолчанию для пропса age.

Передача функций через пропсы

Компоненты могут не только отображать данные, но и вызывать функции. Например:

interface ButtonProps {
  label: string;
  onClick: () => void;
}

function Button(props: ButtonProps) {
  return <button onClick={props.onClick}>{props.label}</button>;
}

// Использование компонента
function handleClick() {
  alert("Кнопка нажата!");
}

<Button label="Нажми меня" onClick={handleClick} />;
  • onClick: () => void — это функция, которая не принимает аргументов и ничего не возвращает.
  • Если кто-то попытается передать, например, строку вместо функции, TypeScript сработает как сторожевой пёс и начнёт лаять.

Комплексные структуры данных

Если ваши компоненты принимают массивы, объекты или вложенные структуры, интерфейсы снова приходят на помощь.

Допустим, у нас есть список задач, и мы передаём его как пропс:

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

interface TaskListProps {
  tasks: Task[];
}

function TaskList({ tasks }: TaskListProps) {
  return (
    <ul>
      {tasks.map((task) => (
        <li key={task.id}>
          {task.title} - {task.completed ? "Сделано" : "В процессе"}
        </li>
      ))}
    </ul>
  );
}

// Использование компонента
const tasks = [
  { id: 1, title: "Выучить TypeScript", completed: true },
  { id: 2, title: "Написать код", completed: false },
];

<TaskList tasks={tasks} />;

Здесь интерфейс Task описывает, какими должны быть объекты в массиве tasks.

Пример: добавляем пропсы в наше приложение

Давайте доработаем наше приложение. Мы создадим компонент Counter, который будет отображать текущее значение счётчика и кнопку для увеличения.

  1. Создаём интерфейс для пропсов:
interface CounterProps {
  initialValue: number;
}
  1. Реализуем компонент:
function Counter({ initialValue }: CounterProps) {
  const [count, setCount] = React.useState(initialValue);

  return (
    <div>
      <p>Текущее значение: {count}</p>
      <button onClick={() => setCount(count + 1)}>Увеличить</button>
    </div>
  );
}
  1. Используем компонент:
<Counter initialValue={0} />;

Здесь initialValue типизирован как число, и его нельзя пропустить.

Типизация с React.FC

В React часто используется специальный тип React.FC (Function Component). Он автоматически добавляет типизацию для пропсов и детей:

interface GreetingProps {
  name: string;
}

const Greeting: React.FC<GreetingProps> = ({ name }) => {
  return <h1>Привет, {name}!</h1>;
};

Хотя это удобно, есть небольшой нюанс: React.FC автоматически добавляет проп children даже если вы его не используете. Это может привести к путанице, поэтому руководства всё чаще рекомендуют избегать этого типа и писать явно. Выбор за вами!

Типичные ошибки

  1. Пропущенные типы: часто забывают типизировать пропсы или делают их слишком общими any. Это убирает всю магию TypeScript.
  2. Избыточные или некорректные значения: если вы передаёте пропсы, не описанные в интерфейсе, TypeScript тоже не оставит это без внимания.
  3. Неправильная обработка необязательных пропсов: если вы используете необязательные пропсы, проверяйте их на undefined, прежде чем использовать.

Сегодня вы узнали, как типизировать пропсы компонентов с помощью интерфейсов. Это невероятно мощный инструмент, который сделает ваш код надёжным, понятным и безопасным. В реальных проектах такая типизация поможет избежать множества ошибок и значительно упростит работу в команде.

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