Типизация props

Fresh — это современный фреймворк для построения веб-приложений на Deno с акцентом на производительность и рендеринг на стороне сервера. Одним из ключевых аспектов разработки компонентов в Fresh является строгая типизация props, что обеспечивает предсказуемость поведения приложения и уменьшает вероятность ошибок на этапе компиляции.

Основы props

В Fresh компоненты создаются как функции, возвращающие JSX. Props передаются в компонент как объект параметров. Простейший пример:

interface ButtonProps {
  label: string;
  disabled?: boolean;
}

export default function Button({ label, disabled }: ButtonProps) {
  return (
    <button disabled={disabled}>
      {label}
    </button>
  );
}

Ключевой момент: props описываются через интерфейсы или типы TypeScript. Это позволяет:

  • Явно указывать обязательные и необязательные параметры.
  • Получать автодополнение в редакторе.
  • Выявлять ошибки типов на этапе компиляции.

Обязательные и необязательные props

Props могут быть обязательными или необязательными. Обязательные props указываются без ?, необязательные — с ?.

interface CardProps {
  title: string;      // обязательный
  subtitle?: string;  // необязательный
}

При передаче props TypeScript гарантирует, что обязательные поля присутствуют:

<Card title="Главная страница" />  // корректно
<Card />                            // ошибка компиляции

Типизация функций в props

Компоненты часто получают функции через props, например обработчики событий:

interface InputProps {
  value: string;
  onChange: (value: string) => void;
}

export default function Input({ value, onChange }: InputProps) {
  return <input value={value} onIn put={(e) => onChange((e.target as HTMLInputElement).value)} />;
}

Типизация функции позволяет строго определить сигнатуру обработчика и избежать ошибок передачи параметров.

Наследование и расширение props

Иногда требуется использовать базовые props и расширять их для других компонентов. В Fresh это делается через наследование интерфейсов:

interface BaseButtonProps {
  disabled?: boolean;
}

interface IconButtonProps extends BaseButtonProps {
  icon: string;
  onClick: () => void;
}

Такой подход улучшает читаемость кода и облегчает повторное использование компонентов.

Props с union-типами и enum

Для ограниченного набора значений удобно использовать union-тип или enum:

type Size = 'small' | 'medium' | 'large';

interface BadgeProps {
  text: string;
  size?: Size;
}

export default function Badge({ text, size = 'medium' }: BadgeProps) {
  return <span className={`badge-${size}`}>{text}</span>;
}

Использование union-типов обеспечивает проверку корректности значения еще на этапе компиляции и упрощает поддержку кода.

Default значения props

Для props можно задавать значения по умолчанию прямо в параметрах функции компонента:

interface AlertProps {
  message: string;
  type?: 'info' | 'warning' | 'error';
}

export default function Alert({ message, type = 'info' }: AlertProps) {
  return <div className={`alert alert-${type}`}>{message}</div>;
}

Это удобно для создания компонентов с предсказуемым поведением, даже если часть props не передана.

Комплексные и вложенные props

Props могут быть сложными объектами или массивами объектов:

interface User {
  id: number;
  name: string;
}

interface UserListProps {
  users: User[];
}

export default function UserList({ users }: UserListProps) {
  return (
    <ul>
      {users.map(user => <li key={user.id}>{user.name}</li>)}
    </ul>
  );
}

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

Рекомендации по организации props

  • Использовать интерфейсы вместо any.
  • Явно указывать обязательные и необязательные поля.
  • Применять union-тип или enum для ограниченного набора значений.
  • Задавать default-значения там, где это возможно.
  • При передаче функций строго типизировать сигнатуру.
  • Разделять props на базовые и расширяемые для повторного использования.

Заключение по типизации в Fresh

Строгая типизация props в Fresh повышает надежность компонентов, упрощает сопровождение кода и уменьшает вероятность ошибок на этапе разработки. Комбинация интерфейсов, union-типов, default-значений и наследования позволяет создавать гибкие и безопасные компоненты, соответствующие современным стандартам TypeScript и практикам frontend-разработки.