Atomico — это современная библиотека для создания веб-компонентов на базе JavaScript, которая использует реактивный подход и функциональные компоненты. Одним из важных инструментов, повышающих гибкость компонентов, являются дженерики. Они позволяют задавать типы данных, с которыми будет работать компонент, делая его более универсальным и безопасным в типизации.
Компоненты в Atomico создаются функцией component(),
которая возвращает объект с описанием веб-компонента. Дженерики
позволяют задавать типы props, events
и ref, что делает компоненты строго
типизированными.
Пример использования дженерика для пропсов:
import { component, html, Props } from "atomico";
type ButtonProps<T = string> = {
label: T;
disabled?: boolean;
};
function Button<T extends string = string>({ label, disabled }: Props<ButtonProps<T>>) {
return html`
<button ?disabled=${disabled}>${label}</button>
`;
}
customElements.define("my-button", component(Button));
В этом примере T задаёт тип для label. Если
компонент используется без указания типа, по умолчанию это
string. Такой подход позволяет создавать универсальные
кнопки, которые могут отображать текст или другие типы данных.
Atomico поддерживает строгую типизацию событий через дженерики. Это особенно важно для компонентов, которые генерируют кастомные события.
type SelectEvent<T> = CustomEvent<{ value: T }>;
function Select<T>({ options }: Props<{ options: T[] }>) {
function handleChange(event: Event) {
const value = (event.target as HTMLSelectElement).value as unknown as T;
dispatch<SelectEvent<T>>(element, "change", { value });
}
return html`
<SELECT onCha nge=${handleChange}>
${options.map(option => html`<option value=${option}>${option}</option>`)}
</select>
`;
}
Здесь Select<T> может работать с любыми типами
данных, а событие change строго типизировано. TypeScript
будет проверять, что value соответствует типу
T.
С помощью дженериков можно типизировать ref — ссылку на DOM-элемент или компонент. Это позволяет безопасно работать с элементами внутри функционального компонента.
import { useRef } FROM "atomico";
function Input<T extends HTMLInputElement>() {
const inputRef = useRef<T>();
function focusInput() {
inputRef.current?.focus();
}
return html`
<input ref=${inputRef} />
<button onCl ick=${focusInput}>Focus</button>
`;
}
В этом примере inputRef строго типизирован как
HTMLInputElement, что исключает ошибки при работе с
DOM-методами.
Дженерики можно комбинировать для создания сложных, универсальных компонентов. Например, компонент формы, который принимает поля разных типов и генерирует события с данными формы:
type Field<T> = {
name: string;
value: T;
};
type FormProps<TFields extends Record<string, any>> = {
fields: { [K in keyof TFields]: Field<TFields[K]> };
};
function Form<TFields>({ fields }: Props<FormProps<TFields>>) {
function handleSubmit() {
const values: TFields = {} as TFields;
Object.keys(fields).forEach(key => {
values[key as keyof TFields] = fields[key as keyof TFields].value;
});
dispatch<CustomEvent<TFields>>(element, "submit", values);
}
return html`
<form onSub mit=${handleSubmit}>
${Object.values(fields).map(
field => html`<input name=${field.name} value=${field.value} />`
)}
<button type="submit">Submit</button>
</form>
`;
}
Такой компонент может использоваться для любой формы с разными полями, при этом TypeScript будет проверять соответствие типов данных.
extends, если
требуется использовать специфические методы или свойства типов.Props,
Ref и событий.ref),
чтобы избежать ошибок.Дженерики в Atomico повышают гибкость компонентов, обеспечивают строгую типизацию и позволяют создавать повторно используемые универсальные элементы интерфейса без потери безопасности типов. Они становятся особенно ценными в больших проектах, где строгая типизация критична для надежности кода.