Haunted — это библиотека для создания веб-компонентов с использованием реактивного подхода и хуков, построенная поверх стандартных Web Components. Одной из ключевых задач при работе с Haunted является корректная типизация компонентов, которая обеспечивает предсказуемое поведение, улучшает читаемость кода и снижает количество ошибок на этапе компиляции.
В Haunted компоненты создаются с помощью функции
component. Тип компонента определяется через параметры,
передаваемые этой функции:
import { component, html } from 'haunted';
const MyComponent = component((props) => html`<p>${props.name}</p>`);
Ключевой момент: props — это объект с
набором свойств, которые передаются компоненту. Их можно типизировать с
помощью TypeScript для строгой проверки:
interface MyComponentProps {
name: string;
age?: number; // необязательное свойство
}
const MyComponent = component<MyComponentProps>((props) => html`
<p>${props.name}, ${props.age ?? 'Возраст не указан'}</p>
`);
Использование интерфейсов позволяет сразу видеть, какие свойства обязательны, а какие опциональны, и получать автокомплит в редакторе.
Haunted поддерживает стандартные хуки, аналогичные React:
useState, useEffect, useReducer и
другие. Для строгой типизации состояния или возвращаемых значений хуков
можно использовать дженерики:
import { useState } from 'haunted';
const Counter = component(() => {
const [count, setCount] = useState<number>(0);
return html`<button @click=${() => setCount(count + 1)}>Счетчик: ${count}</button>`;
});
Особенности:
useState<T> позволяет указать тип состояния.useReducer<S, A> позволяет определить тип
состояния S и тип действий A.useRef<T> можно задавать тип элементов DOM
или пользовательских объектов.События в Haunted могут быть кастомными, что делает их важной частью
типизации. Для типизации события используют интерфейсы с полями
detail и type:
interface IncrementEventDetail {
step: number;
}
const Counter = component(() => {
const handleClick = (e: CustomEvent<IncrementEventDetail>) => {
console.log(e.detail.step);
};
return html`<button @click=${handleClick}>Увеличить</button>`;
});
Совет: При генерации событий через
dispatchEvent следует явно указывать тип события:
const incrementEvent: CustomEvent<IncrementEventDetail> = new CustomEvent('increment', { detail: { step: 1 } });
element.dispatchEvent(incrementEvent);
Это гарантирует согласованность типов между компонентом и его пользователями.
Haunted поддерживает стандартные Web Components слоты. Для строгой типизации содержимого слотов можно использовать интерфейсы и дженерики:
interface SlotProps {
header?: HTMLElement;
footer?: HTMLElement;
}
const Layout = component<SlotProps>((props) => html`
<header><slot name="header">${props.header}</slot></header>
<main><slot></slot></main>
<footer><slot name="footer">${props.footer}</slot></footer>
`);
Слоты могут быть динамическими, поэтому важно указывать допустимые типы контента, чтобы избежать ошибок при доступе к ним из кода компонента.
Для доступа к элементам DOM в Haunted используется
useRef или ref атрибуты. Типизация позволяет
работать с конкретными элементами безопасно:
import { useRef } from 'haunted';
const InputComponent = component(() => {
const inputRef = useRef<HTMLInputElement>(null);
const focusInput = () => {
inputRef.current?.focus();
};
return html`<input ${ref(inputRef)} type="text" /><button @click=${focusInput}>Фокус</button>`;
});
Ключевой момент: использование
<HTMLInputElement> вместо any
гарантирует корректное обращение к свойствам элемента, таким как
.value, .focus() и другим.
Haunted позволяет использовать дженерики для создания компонентов с гибкой типизацией. Это особенно полезно для переиспользуемых библиотечных компонентов:
interface ListProps<T> {
items: T[];
renderItem: (item: T) => string;
}
const List = component<ListProps<any>>((props) => html`
<ul>
${props.items.map(item => html`<li>${props.renderItem(item)}</li>`)}
</ul>
`);
С дженериками можно создавать универсальные компоненты:
const NumberList = component<ListProps<number>>((props) => html`
<ul>${props.items.map(item => html`<li>${item}</li>`)}</ul>
`);
Полная интеграция Haunted с TypeScript позволяет:
props, state,
refs.Пример: строгая типизация компонента с состоянием, событиями и слотом:
interface CardProps {
title: string;
onClose: (e: CustomEvent<void>) => void;
}
const Card = component<CardProps>((props) => {
const [visible, setVisible] = useState<boolean>(true);
const closeCard = () => {
setVisible(false);
props.onClose(new CustomEvent('close'));
};
return visible ? html`
<div class="card">
<header><slot name="title">${props.title}</slot></header>
<button @click=${closeCard}>Закрыть</button>
</div>
` : null;
});
Типизация в Haunted охватывает все основные аспекты: свойства, состояние, события, слоты и ссылки на DOM. Она обеспечивает строгую проверку на этапе компиляции и делает компоненты безопасными и предсказуемыми в работе. Правильная типизация повышает читаемость кода и упрощает поддержку сложных приложений.