Slim.js предоставляет мощный и легковесный подход к созданию веб-компонентов с реактивным обновлением данных. Одной из ключевых особенностей при разработке масштабируемых и переиспользуемых компонентов является использование дженериков, которые позволяют задавать тип данных, с которыми компонент будет работать.
Дженерики позволяют компоненту быть типизированным, сохраняя при этом гибкость в отношении данных, которые он может принимать. В стандартном JavaScript нет строгой типизации, но в сочетании с TypeScript дженерики становятся инструментом безопасного программирования.
Пример базового объявления компонента с дженериком:
import { Slim, html } from 'slim-js';
interface Item<T> {
value: T;
}
class ListComponent<T> extends Slim {
items: Item<T>[] = [];
template() {
return html`
<ul>
${this.items.map(item => html`<li>${item.value}</li>`)}
</ul>
`;
}
}
customElements.define('list-component', ListComponent);
В этом примере ListComponent принимает массив элементов
с типом T. Это позволяет создавать списки с разными типами
данных без изменения внутренней логики компонента.
Дженерики могут быть ограничены с помощью ключевого слова
extends. Это позволяет компоненту работать только с
определёнными типами данных, обеспечивая дополнительную
безопасность.
interface HasId {
id: number;
}
class IdListComponent<T extends HasId> extends Slim {
items: T[] = [];
template() {
return html`
<ul>
${this.items.map(item => html`<li>ID: ${item.id}</li>`)}
</ul>
`;
}
}
customElements.define('id-list-component', IdListComponent);
Здесь T ограничен интерфейсом HasId, что
гарантирует наличие свойства id у всех элементов
списка.
Slim.js использует реактивную привязку данных, поэтому дженерики
легко интегрируются с системой обновления компонентов. Любое изменение в
массиве items автоматически перерисует DOM:
const component = document.createElement('id-list-component') as IdListComponent<{ id: number; name: string }>;
component.items = [
{ id: 1, name: 'Первый' },
{ id: 2, name: 'Второй' }
];
document.body.appendChild(component);
// Добавление нового элемента
component.items.push({ id: 3, name: 'Третий' });
component.triggerUpdate(); // Slim.js отслеживает изменения
Использование дженериков гарантирует, что каждый элемент списка соответствует ожидаемой структуре, а TypeScript сразу сигнализирует о нарушениях типа.
Компоненты могут принимать пропсы и эмитировать события с дженериками, что делает их максимально универсальными.
interface ButtonProps<T> {
label: string;
payload: T;
}
class GenericButton<T> extends Slim {
props: ButtonProps<T>;
handleClick() {
this.emit('button-click', this.props.payload);
}
template() {
return html`<button @click=${() => this.handleClick()}>${this.props.label}</button>`;
}
}
customElements.define('generic-button', GenericButton);
Такой подход позволяет создавать кнопки, которые передают данные любого типа, сохраняя строгую типизацию.
Дженерики хорошо сочетаются с наследованием, позволяя создавать специализированные компоненты без дублирования кода:
class SelectComponent<T> extends Slim {
options: T[] = [];
}
class UserSelectComponent extends SelectComponent<{ id: number; name: string }> {
template() {
return html`
<select>
${this.options.map(user => html`<option value=${user.id}>${user.name}</option>`)}
</select>
`;
}
}
Базовый класс SelectComponent может работать с любыми
типами, а дочерний UserSelectComponent фиксирует конкретную
структуру данных.
Дженерики в Slim.js открывают возможности для построения сложных интерфейсных компонентов с высокой степенью типовой безопасности, облегчая масштабирование приложений и поддержку кода.