TypeScript в экосистеме Kepler.gl используется не как вспомогательный слой, а как структурный каркас, обеспечивающий согласованность данных между визуализацией, состоянием приложения и графическим рендерингом на базе deck.gl. Типизация охватывает слои Redux-архитектуры, конфигурации слоёв карты, описание датасетов и взаимодействие с геоданными.
Основная сложность заключается в том, что Kepler.gl объединяет несколько доменов: геопространственные данные, декларативные визуализации и состояние интерфейса. Без строгих типов эти области быстро начинают конфликтовать на уровне контрактов.
Типизация в Kepler.gl строится вокруг нескольких фундаментальных идей:
any в критических слояхКлючевой целью является предотвращение ошибок на этапе компиляции при трансформации данных между слоями карты и визуализацией.
Основная единица данных — датасет, который описывает таблицу с географическими координатами и атрибутами.
Типовая модель:
export type KeplerTableRow = {
[key: string]: string | number | boolean | null;
};
export interface KeplerDataset {
id: string;
label: string;
color?: number[];
dat a: KeplerTableRow[];
fields: KeplerField[];
}
Описание полей:
export interface KeplerField {
name: string;
type: 'string' | 'integer' | 'real' | 'timestamp' | 'boolean';
format?: string;
}
Типизация полей критична для корректной работы агрегаций, фильтров и визуальных трансформаций.
Геоданные в Kepler.gl опираются на расширенные GeoJSON-конструкции. Типы обычно дополняются координатными контрактами.
export type LonLat = [number, number];
export interface GeoPoint {
type: 'Point';
coordinates: LonLat;
}
export interface GeoLine {
type: 'LineString';
coordinates: LonLat[];
}
export interface GeoPolygon {
type: 'Polygon';
coordinates: LonLat[][];
}
Для унификации:
export type GeoFeature = GeoPoint | GeoLine | GeoPolygon;
В реальных сценариях типы часто расширяются дополнительными свойствами через generics:
export interface GeoFeatureWithProps<T = Record<string, unknown>> {
type: string;
geometry: GeoFeature;
properties: T;
}
Архитектура Kepler.gl построена вокруг глобального состояния, содержащего карты, датасеты, UI-состояния и конфигурации слоёв.
export interface KeplerGlState {
visState: VisState;
mapState: MapState;
uiState: UiState;
}
Отвечает за визуализацию:
export interface VisState {
datasets: Record<string, KeplerDataset>;
layers: LayerState[];
filters: FilterState[];
interactionConfig: InteractionConfig;
}
export interface MapState {
latitude: number;
longitude: number;
zoom: number;
bearing: number;
pitch: number;
}
export interface UiState {
activeSidePanel: string | null;
currentModal: string | null;
readOnly: boolean;
}
Слой — ключевая абстракция, связывающая данные и визуальное представление. Каждый слой имеет собственную конфигурацию.
export interface BaseLayerConfig {
id: string;
type: string;
dataId: string;
label: string;
isVisible: boolean;
}
Расширенные слои:
export interface PointLayerConfig extends BaseLayerConfig {
type: 'point';
radius: number;
color: number[];
}
export interface LineLayerConfig extends BaseLayerConfig {
type: 'line';
thickness: number;
color: number[];
}
export type LayerState = PointLayerConfig | LineLayerConfig;
Типизация слоя напрямую влияет на генерацию deck.gl-подслоёв, где каждая конфигурация транслируется в WebGL-рендеринг.
Фильтрация данных требует строгого описания диапазонов и типов полей.
export interface BaseFilter {
id: string;
dataId: string;
name: string;
}
export interface RangeFilter extends BaseFilter {
type: 'range';
value: [number, number];
}
export interface TimeFilter extends BaseFilter {
type: 'timeRange';
value: [number, number];
}
export type FilterState = RangeFilter | TimeFilter;
Типизация фильтров критична для предотвращения неконсистентных состояний UI и данных.
Интерактивность карты описывается через конфигурационные объекты:
export interface InteractionConfig {
tooltip: boolean;
brush: boolean;
geocoder: boolean;
}
Расширение через строгие булевые флаги позволяет контролировать поведение UI без побочных эффектов.
Redux actions в Kepler.gl типизируются через discriminated unions.
export interface AddDatasetAction {
type: 'ADD_DATASET';
payload: KeplerDataset;
}
export interface UpdateMapStateAction {
type: 'UPDATE_MAP_STATE';
payload: Partial<MapState>;
}
export type KeplerAction =
| AddDatasetAction
| UpdateMapStateAction;
Такой подход позволяет reducer-ам безопасно обрабатывать действия без runtime-проверок.
export function visStateReducer(
state: VisState,
action: KeplerAction
): VisState {
switch (action.type) {
case 'ADD_DATASET':
return {
...state,
datasets: {
...state.datasets,
[action.payload.id]: action.payload
}
};
default:
return state;
}
}
Типизация здесь предотвращает несоответствие структуры состояния при обновлениях.
Generics применяются для расширения типов данных без потери строгой структуры.
export interface TypedDataset<T = KeplerTableRow> {
id: string;
dat a: T[];
}
Это позволяет адаптировать Kepler.gl под специфические доменные модели, сохраняя совместимость с визуализацией.
Kepler.gl часто интегрируется в сторонние приложения, где требуется расширение базовых интерфейсов через declaration merging.
declare module 'kepler.gl' {
interface KeplerGlState {
customPluginState?: unknown;
}
}
Такой механизм позволяет внедрять плагины без модификации ядра библиотеки.
Конфигурации слоёв и визуальных параметров часто сериализуются, поэтому требуют стабильных контрактов:
export interface LayerVisualConfig {
opacity: number;
strokeColor: [number, number, number];
fillColor: [number, number, number];
}
Эти типы используются при сохранении и восстановлении проектов карт.
deck.gl предоставляет собственную систему типов для слоёв WebGL, которые в Kepler.gl выступают нижним уровнем абстракции.
Типичный мост:
import { ScatterplotLayer } from '@deck.gl/layers';
export interface DeckLayerProps {
id: string;
dat a: any[];
getPosition: (d: any) => [number, number];
}
Kepler.gl преобразует собственные LayerConfig в deck.gl props через адаптеры с типизированными контрактами.
Интеграция с картографическим движком Mapbox GL JS требует описания viewport-состояния и стилей:
export interface MapViewport {
longitude: number;
latitude: number;
zoom: number;
pitch: number;
bearing: number;
}
Эти типы синхронизируются с Redux state и UI слоями.
Событийная модель описывается через строгие интерфейсы:
export interface LayerClickEvent {
layerId: string;
object: unknown;
coordinates: [number, number];
}
export type KeplerEvent = LayerClickEvent;
Это позволяет унифицировать обработчики интерактивности.
anyВ крупных частях Kepler.gl any заменяется на:
unknown для небезопасных входовПример mapped type:
type OptionalConfig<T> = {
[K in keyof T]?: T[K];
};
При сохранении проектов требуется строгая структура:
export interface KeplerMapConfig {
version: string;
config: KeplerGlState;
}
Это гарантирует обратную совместимость между версиями состояния и визуализацией.