Tom Select в стандартной конфигурации опирается на простую модель
данных, где ключевыми сущностями выступают Option,
Item и конфигурационный объект Settings. При
переходе к масштабируемым интерфейсам этой модели становится
недостаточно, и возникает необходимость расширения типов без нарушения
внутренней логики библиотеки.
В базовом виде Option представляет объект, содержащий
как минимум идентификатор и отображаемое значение:
value: string | numbertext: stringItem обычно является упрощённой проекцией
Option, используемой внутри выбранных значений. Однако на
практике эти структуры редко остаются примитивными. Почти всегда
требуется добавление бизнес-полей: статус, метки, вложенные данные,
связи с внешними сущностями.
Расширение типов в TypeScript строится вокруг принципа структурного расширения, а не наследования. Это позволяет адаптировать модель данных без изменения исходного кода библиотеки.
Наиболее распространённый сценарий — добавление пользовательских
полей в Option. Например, если список содержит
пользователей:
type UserOption = {
value: string;
text: string;
role: string;
department: string;
isActive: boolean;
};
В таком случае необходимо синхронизировать типы Option и
Item, поскольку Tom Select может преобразовывать данные
между состояниями.
Типизация обычно строится через дженерики (если обёртка поддерживает их):
interface MyOption {
value: string;
text: string;
meta: {
role: string;
department: string;
};
}
При этом важно учитывать, что внутренняя логика библиотеки опирается
не на строгую типизацию, а на наличие ключевых полей. Поэтому
расширенные свойства должны быть неразрушающими: они не
должны заменять обязательные поля value и
text, а лишь дополнять их.
При работе с доменными моделями часто возникает необходимость вложенных структур. Например, опции могут включать связи:
Типизация таких структур требует аккуратного подхода:
type DepartmentOption = {
value: string;
text: string;
organization: {
id: string;
name: string;
};
};
В подобных случаях text может перестать быть достаточным
для отображения, и его формирование переносится в слой
render.
Конфигурационный объект Settings в Tom Select является
одним из ключевых мест расширения поведения. Он включает:
При расширении типов конфигурации важно учитывать, что часть параметров может быть добавлена через плагины или пользовательские расширения.
Пример расширенного интерфейса:
interface ExtendedSettings {
maxItems: number;
searchField: string[];
debounceTime: number;
allowCreate: boolean;
normalizeInput: (input: string) => string;
}
Особое значение имеет типизация функций, поскольку именно они определяют точки расширения логики. Например:
score: (search: string, option: Option) => numberfilter: (option: Option) => booleanЭти функции фактически превращают конфигурацию в поведенческий слой.
Механизм render в Tom Select позволяет полностью
переопределять представление элементов интерфейса. Это один из наиболее
гибких механизмов расширения, но и один из наиболее сложных с точки
зрения типизации.
Типичная структура:
interface RenderTemplates<TOption> {
option: (data: TOption, escape: (str: string) => string) => string;
item: (data: TOption, escape: (str: string) => string) => string;
option_create?: (data: { input: string }) => string;
}
При расширении типов Option необходимо синхронизировать
их с render, иначе возникает рассинхронизация между данными
и отображением.
Ключевой момент: render не ограничивает структуру
данных, но ожидает её стабильность. Это означает, что изменение формы
Option должно сопровождаться изменением всех
render-функций.
События в Tom Select представляют собой важный механизм интеграции с внешними системами. Типизация событий позволяет формировать предсказуемое поведение.
Примеры событий:
changeitem_additem_removeoption_adddropdown_openПри расширении часто требуется добавление пользовательских событий через композицию:
type TomSelectEvents<T> = {
change: (value: T[]) => void;
item_add: (value: T) => void;
item_remove: (value: T) => void;
};
Расширение событийной модели особенно важно при интеграции с архитектурами:
События становятся мостом между внутренним состоянием компонента и внешней логикой приложения.
Плагинная архитектура Tom Select позволяет добавлять новые возможности без модификации ядра. Однако типизация плагинов требует особого подхода, поскольку плагины могут:
Пример типизации плагина:
interface PluginContext<T> {
select: TomSelect<T>;
settings: ExtendedSettings;
}
Плагин может расширять интерфейс экземпляра:
interface TomSelect<T> {
customMethod(): void;
}
Это достигается через module augmentation, что позволяет безопасно добавлять методы без конфликтов типов.
В TypeScript основной механизм расширения библиотек — декларативное слияние модулей.
declare module "tom-select" {
interface TomSelect<T> {
refreshData(): void;
}
}
Такой подход позволяет:
Особенность заключается в том, что расширение происходит глобально для всех экземпляров, что требует аккуратного проектирования архитектуры.
Несмотря на то, что типизация работает на уровне компиляции, сама библиотека позволяет расширять поведение через прототип.
Пример:
TomSelect.prototype.refreshData = function () {
this.clearOptions();
this.load(callback);
};
При этом TypeScript-типизация должна быть синхронизирована вручную через augmentation.
Такой подход создаёт расхождение между runtime и compile-time моделью, которое необходимо контролировать.
При работе с реальными системами данные редко бывают плоскими. Расширенные типы часто включают:
Пример:
type ProductOption = {
value: string;
text: string;
price: number;
currency: string;
stock: {
available: number;
reserved: number;
};
category: {
id: string;
name: string;
};
};
В таких моделях Tom Select выступает только как слой выбора, а вся сложность переносится в типы данных.
При использовании удалённых источников данных типизация усложняется за счёт промежуточных состояний:
Тип функции загрузки:
type LoadFunction<T> = (query: string, callback: (items: T[]) => void) => void;
Расширенные варианты включают Promise:
type AsyncLoad<T> = (query: string) => Promise<T[]>;
Это позволяет интегрировать Tom Select с современными API-архитектурами.
При расширении базовых типов важно учитывать обратную совместимость. Любое расширение должно:
value и
textДеградация типов часто проявляется при смешивании разных источников данных, когда часть опций имеет расширенную структуру, а часть — базовую. В таких случаях применяется объединение:
type MixedOption = BaseOption | ExtendedOption;
или нормализация через адаптер:
function normalize(option: any): BaseOption {
return {
value: option.value,
text: option.text ?? String(option.value)
};
}
В сложных системах Tom Select перестаёт быть просто UI-компонентом и становится частью доменной логики. Это приводит к необходимости формализации контрактов:
Типизация здесь выполняет роль документации поведения, фиксируя ожидания между слоями системы и обеспечивая предсказуемость интеграции.