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

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


Определение типов свойств

В Fresh компоненты могут принимать props — объект с параметрами, который задает поведение или отображение компонента. Типизация props осуществляется с помощью интерфейсов или типов TypeScript.

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

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

Ключевые моменты:

  • label — обязательное свойство строкового типа.
  • disabled — необязательное свойство, по умолчанию false.
  • onClick — необязательная функция-обработчик событий.

Использование интерфейсов позволяет редактору выдавать автодополнение и проверку типов ещё до запуска приложения.


Типизация JSX-элементов

Fresh использует JSX, что требует корректного определения типов возвращаемого значения компонента. В TypeScript для этого используется тип JSX.Element:

import { JSX } from "preact";

interface CardProps {
  title: string;
  content: string;
}

export default function Card({ title, content }: CardProps): JSX.Element {
  return (
    <div class="card">
      <h2>{title}</h2>
      <p>{content}</p>
    </div>
  );
}

Объявление возвращаемого типа как JSX.Element позволяет TypeScript точно знать, что функция возвращает корректный JSX, совместимый с Preact, который лежит в основе Fresh.


Типизация событий и ref

Для интерактивных компонентов важно корректно типизировать события и ссылки на элементы DOM. Fresh совместим с Preact-стилем событий:

interface InputProps {
  value: string;
  onInput: (e: Event) => void;
}

export default function TextInput({ value, onInput }: InputProps): JSX.Element {
  return <input type="text" value={value} onIn put={onInput} />;
}
  • Event — базовый тип события. Для более точной типизации можно использовать InputEvent или KeyboardEvent.
  • Свойство ref также типизируется через RefObject:
import { useRef } from "preact/hooks";

export default function FocusableInput() {
  const inputRef = useRef<HTMLInputElement>(null);

  function focus() {
    inputRef.current?.focus();
  }

  return <input ref={inputRef} />;
}

Типизация серверных компонентов

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

interface UserProfileProps {
  userId: string;
}

export default async function UserProfile({ userId }: UserProfileProps): Promise<JSX.Element> {
  const response = await fetch(`https://api.example.com/users/${userId}`);
  const user = await response.json();

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}

Использование Promise<JSX.Element> гарантирует корректную работу с асинхронными компонентами.


Комбинирование типов и утилиты

TypeScript позволяет создавать более сложные структуры типов, комбинируя интерфейсы и типы с помощью Partial, Pick, Omit и других утилит:

interface BaseProps {
  id: string;
  className?: string;
}

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

type OptionalButtonProps = Partial<ButtonProps>;
  • Partial<ButtonProps> делает все свойства необязательными.
  • Pick<ButtonProps, "label" | "onClick"> позволяет выбрать только определенные свойства.
  • Omit<ButtonProps, "className"> исключает свойство из типа.

Эти утилиты помогают создавать универсальные и повторно используемые компоненты.


Типизация детей (children)

Компоненты в Fresh могут принимать вложенные элементы через children. Тип children в Fresh описывается как JSX.Element | JSX.Element[]:

interface ContainerProps {
  children: JSX.Element | JSX.Element[];
}

export default function Container({ children }: ContainerProps): JSX.Element {
  return <div class="container">{children}</div>;
}

Такое определение позволяет компоненту корректно отображать один или несколько дочерних элементов.


Рекомендации по типизации

  • Использовать интерфейсы для props, особенно для публичных компонентов.
  • Всегда указывать возвращаемый тип JSX.Element или Promise<JSX.Element> для серверных компонентов.
  • Применять утилиты TypeScript для расширения и комбинирования типов.
  • Тщательно типизировать события и ref, чтобы избежать runtime-ошибок.
  • Типизировать children, чтобы компоненты могли безопасно обрабатывать вложенные элементы.

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