Строгая типизация

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

Типы свойств компонента

В Haunted компоненты получают данные через свойства (props). Для строгой типизации важно явно указывать ожидаемый тип каждого свойства. Хотя JavaScript сам по себе динамически типизирован, интеграция с TypeScript позволяет гарантировать корректность типов.

Пример объявления компонента с типизированными свойствами:

import { component, html } from 'haunted';

interface UserCardProps {
  name: string;
  age: number;
  isAdmin?: boolean; // необязательное свойство
}

function UserCard({ name, age, isAdmin }: UserCardProps) {
  return html`
    <div>
      <h2>${name}</h2>
      <p>Возраст: ${age}</p>
      ${isAdmin ? html`<strong>Администратор</strong>` : ''}
    </div>
  `;
}

customElements.define('user-card', component(UserCard));

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

  • Интерфейс UserCardProps задаёт строгие типы для каждого свойства.
  • isAdmin помечено как необязательное, что предотвращает ошибки при отсутствии этого свойства.
  • При попытке передать значение неправильного типа TypeScript выдаст ошибку на этапе компиляции.

Типизация состояния с хуками

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

import { useState } from 'haunted';

function Counter() {
  const [count, setCount] = useState<number>(0);

  const increment = () => setCount(count + 1);

  return html`
    <div>
      <p>Счётчик: ${count}</p>
      <button @click=${increment}>Увеличить</button>
    </div>
  `;
}

Особенности типизации:

  • <number> указывает, что состояние count может быть только числом.
  • Любая попытка присвоить count значение другого типа приведёт к ошибке TypeScript.
  • Можно использовать сложные типы, включая массивы и объекты:
const [todos, setTodos] = useState<{ id: number; title: string; done: boolean }[]>([]);

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

Haunted позволяет подписываться на события через синтаксис @event. Строгая типизация событий позволяет точно описывать структуру данных, передаваемых в обработчик.

interface FormSubmitEventDetail {
  username: string;
  password: string;
}

function LoginForm() {
  const handleSubmit = (event: CustomEvent<FormSubmitEventDetail>) => {
    console.log(event.detail.username);
  };

  return html`
    <form @submit=${(e: Event) => {
      e.preventDefault();
      handleSubmit(new CustomEvent('submit', { detail: { username: 'user', password: '1234' } }));
    }}>
      <input name="username" />
      <input name="password" type="password" />
      <button type="submit">Войти</button>
    </form>
  `;
}

Важные моменты:

  • Используется CustomEvent<T>, где T — тип объекта detail.
  • Обработчик гарантированно получает данные определённого типа, предотвращая ошибки при чтении полей.

Типизация слотов и детей компонентов

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

interface CardProps {
  header: string;
  children?: HTMLElement[];
}

function Card({ header, children }: CardProps) {
  return html`
    <div class="card">
      <h3>${header}</h3>
      <div class="content">
        ${children}
      </div>
    </div>
  `;
}

Особенности:

  • children типизирован как массив HTMLElement.
  • Предотвращает случайную передачу строк или других неподходящих типов.

Совмещение Haunted с TypeScript

Для полного использования строгой типизации рекомендуется:

  • Использовать интерфейсы для свойств и состояния.
  • Типизировать хуки useState, useEffect, useRef.
  • Типизировать события и слоты.
  • Подключать строгие настройки компилятора TypeScript (strict, noImplicitAny, strictNullChecks).

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

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

interface TodoListProps {
  initialTodos?: TodoItem[];
}

function TodoList({ initialTodos = [] }: TodoListProps) {
  const [todos, setTodos] = useState<TodoItem[]>(initialTodos);

  const toggleTodo = (id: number) => {
    setTodos(todos.map(todo => todo.id === id ? { ...todo, completed: !todo.completed } : todo));
  };

  return html`
    <ul>
      ${todos.map(todo => html`
        <li @click=${() => toggleTodo(todo.id)}>
          ${todo.completed ? html`<s>${todo.text}</s>` : todo.text}
        </li>
      `)}
    </ul>
  `;
}

Выводы из практики:

  • Строгая типизация повышает читаемость кода.
  • Позволяет избежать ошибок при передаче данных между компонентами.
  • Улучшает интеграцию с современными инструментами разработки и IDE.

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