Skip to content

Type System

Complete reference for TypeScript types and interfaces in the Route Optimization Map library.

Overview

All packages are written in TypeScript and provide comprehensive type definitions. This guide covers the core types used across the library.

Core Types

LatLng

Geographic coordinates (latitude and longitude).

typescript
interface LatLng {
  lat: number; // Latitude: -90 to 90
  lng: number; // Longitude: -180 to 180
}

Example:

typescript
const bangkok: LatLng = {
  lat: 13.7563,
  lng: 100.5018,
};

Stop

A single location on a route.

typescript
interface Stop {
  id: string;
  location: LatLng;
  name?: string;
  address?: string;
  duration?: number;
  metadata?: Record<string, any>;
}

Example:

typescript
const deliveryStop: Stop = {
  id: 'delivery-001',
  location: { lat: 13.7563, lng: 100.5018 },
  name: 'Customer A',
  address: '123 Sukhumvit Road',
  duration: 15, // minutes
  metadata: {
    orderId: 'ORD-12345',
    priority: 'high',
  },
};

Route

A sequence of stops to visit.

typescript
interface Route {
  stops: Stop[];
  options?: RouteOptions;
}

Example:

typescript
const deliveryRoute: Route = {
  stops: [
    { id: '1', location: { lat: 13.7563, lng: 100.5018 } },
    { id: '2', location: { lat: 13.7467, lng: 100.5352 } },
  ],
  options: {
    polylineOptions: {
      strokeColor: '#FF0000',
      strokeWeight: 6,
    },
  },
};

RouteOptions

Customization options for route rendering.

typescript
interface RouteOptions {
  polylineOptions?: PolylineOptions;
  markerOptions?: MarkerOptions;
  fitBounds?: boolean;
}

interface PolylineOptions {
  strokeColor?: string;
  strokeOpacity?: number;
  strokeWeight?: number;
  geodesic?: boolean;
}

interface MarkerOptions {
  showLabels?: boolean;
  labelColor?: string;
  startMarker?: MarkerConfig;
  endMarker?: MarkerConfig;
  stopMarker?: MarkerConfig;
}

interface MarkerConfig {
  icon?: string | MarkerIcon;
  label?: string | MarkerLabel;
}

Example:

typescript
const options: RouteOptions = {
  polylineOptions: {
    strokeColor: '#4CAF50',
    strokeOpacity: 0.8,
    strokeWeight: 5,
    geodesic: true,
  },
  markerOptions: {
    showLabels: true,
    labelColor: '#FFFFFF',
    startMarker: {
      icon: '/icons/start.png',
      label: 'START',
    },
  },
  fitBounds: true,
};

Map Types

MapConfig

Configuration for map initialization.

typescript
interface MapConfig {
  apiKey: string;
  center?: LatLng;
  zoom?: number;
  mapTypeId?: 'roadmap' | 'satellite' | 'hybrid' | 'terrain';
  styles?: MapStyle[];
  restriction?: MapRestriction;
  gestureHandling?: 'cooperative' | 'greedy' | 'none' | 'auto';
}

Example:

typescript
const mapConfig: MapConfig = {
  apiKey: 'YOUR_API_KEY',
  center: { lat: 13.7563, lng: 100.5018 },
  zoom: 12,
  mapTypeId: 'roadmap',
  gestureHandling: 'cooperative',
};

MapBounds

Geographic boundaries.

typescript
interface MapBounds {
  north: number;
  south: number;
  east: number;
  west: number;
}

Example:

typescript
const thailandBounds: MapBounds = {
  north: 20.5,
  south: 5.5,
  east: 106.0,
  west: 97.0,
};

Optimization Types

OptimizationRequest

Request for route optimization.

typescript
interface OptimizationRequest {
  shipments: Shipment[];
  vehicles: OptimizationVehicle[];
  globalStartTime?: string;
  globalEndTime?: string;
  searchMode?: 'RETURN_FAST' | 'CONSUME_ALL_AVAILABLE_TIME';
}

interface Shipment {
  id: string;
  pickupLocation?: OptimizationLocation | OptimizationLocation[];
  deliveryLocation?: OptimizationLocation | OptimizationLocation[];
  loadDemands?: Record<string, number>;
  pickupTimeWindows?: TimeWindow[];
  deliveryTimeWindows?: TimeWindow[];
}

interface OptimizationLocation {
  latitude: number;
  longitude: number;
}

interface OptimizationVehicle {
  id: string;
  startLocation?: OptimizationLocation;
  endLocation?: OptimizationLocation;
  loadLimits?: LoadLimit[];
  costPerKilometer?: number;
  costPerHour?: number;
  travelMode?: 'DRIVING' | 'WALKING' | 'BICYCLING';
}

interface TimeWindow {
  startTime: string; // ISO 8601 format
  endTime: string;
}

interface LoadLimit {
  type: string;
  maxLoad?: number;
  softMaxLoad?: number;
  costPerUnitAboveSoftMax?: number;
}

Example:

typescript
const optimizationRequest: OptimizationRequest = {
  shipments: [
    {
      id: 'shipment-1',
      deliveryLocation: {
        latitude: 13.7563,
        longitude: 100.5018,
      },
      loadDemands: { weight: 50 },
      deliveryTimeWindows: [
        {
          startTime: '2025-12-12T09:00:00Z',
          endTime: '2025-12-12T17:00:00Z',
        },
      ],
    },
  ],
  vehicles: [
    {
      id: 'vehicle-1',
      startLocation: {
        latitude: 13.7563,
        longitude: 100.5018,
      },
      loadLimits: [
        {
          type: 'weight',
          maxLoad: 1000,
        },
      ],
      travelMode: 'DRIVING',
    },
  ],
  searchMode: 'RETURN_FAST',
};

OptimizationResponse

Response from route optimization.

typescript
interface OptimizationResponse {
  success: boolean;
  routes?: OptimizedRoute[];
  metrics?: OptimizationMetrics;
  statistics?: OptimizationStatistics;
  error?: string;
}

interface OptimizedRoute {
  vehicleId: string;
  stops?: OptimizedStop[];
  metrics?: RouteMetrics;
}

interface OptimizedStop {
  shipmentId?: string;
  location?: OptimizationLocation;
  arrivalTime?: string;
  departureTime?: string;
  loadAfterStop?: Record<string, number>;
}

interface OptimizationMetrics {
  totalDistance?: number;
  totalDuration?: number;
  totalCost?: number;
}

interface OptimizationStatistics {
  usedVehicleCount: number;
  earliestVehicleStartTime?: string;
  latestVehicleEndTime?: string;
}

Example:

typescript
const response: OptimizationResponse = {
  success: true,
  routes: [
    {
      vehicleId: 'vehicle-1',
      stops: [
        {
          shipmentId: 'shipment-1',
          location: { latitude: 13.7563, longitude: 100.5018 },
          arrivalTime: '2025-12-12T09:30:00Z',
          departureTime: '2025-12-12T09:45:00Z',
        },
      ],
      metrics: {
        distance: 15.5,
        duration: 1800,
      },
    },
  ],
  metrics: {
    totalDistance: 15.5,
    totalDuration: 1800,
    totalCost: 250,
  },
};

Framework-Specific Types

React Types

typescript
// useRouteMap
interface UseRouteMapOptions extends MapConfig {
  onError?: (error: Error) => void;
}

interface UseRouteMapReturn {
  mapRef: RefObject<HTMLDivElement>;
  map: google.maps.Map | null;
  isReady: boolean;
  renderRoute: (route: Route) => void;
  clearRoute: () => void;
  addMarker: (location: LatLng, options?: MarkerOptions) => void;
  removeMarker: (id: string) => void;
  clearMarkers: () => void;
}

// useMapControls
interface UseMapControlsReturn {
  center: LatLng | null;
  zoom: number | null;
  bounds: MapBounds | null;
  setCenter: (location: LatLng) => void;
  setZoom: (zoom: number) => void;
  fitBounds: (bounds: MapBounds) => void;
}

// useRouteOptimization
interface UseRouteOptimizationResult {
  status: OptimizationStatus;
  data: OptimizationResponse | null;
  error: string | null;
  isLoading: boolean;
  isSuccess: boolean;
  isError: boolean;
  optimize: (request: OptimizationRequest) => Promise<OptimizationResponse>;
  reset: () => void;
  reinitialize: (config: RouteCalculatorConfig) => Promise<void>;
}

type OptimizationStatus = 'idle' | 'initializing' | 'optimizing' | 'success' | 'error';

Vue Types

typescript
// useRouteMap
interface UseRouteMapOptions extends MapConfig {
  mapElement: Ref<HTMLDivElement | undefined>;
  onError?: (error: Error) => void;
}

interface UseRouteMapReturn {
  map: Ref<google.maps.Map | null>;
  isReady: Ref<boolean>;
  renderRoute: (route: Route) => void;
  clearRoute: () => void;
  addMarker: (location: LatLng, options?: MarkerOptions) => void;
  removeMarker: (id: string) => void;
  clearMarkers: () => void;
}

// useMapControls
interface UseMapControlsReturn {
  center: Ref<LatLng | null>;
  zoom: Ref<number | null>;
  bounds: Ref<MapBounds | null>;
  setCenter: (location: LatLng) => void;
  setZoom: (zoom: number) => void;
  fitBounds: (bounds: MapBounds) => void;
}

// useRouteOptimization
interface UseRouteOptimizationResult {
  status: Ref<OptimizationStatus>;
  data: Ref<OptimizationResponse | null>;
  error: Ref<string | null>;
  isLoading: Readonly<Ref<boolean>>;
  isSuccess: Readonly<Ref<boolean>>;
  isError: Readonly<Ref<boolean>>;
  optimize: (request: OptimizationRequest) => Promise<OptimizationResponse>;
  reset: () => void;
  reinitialize: (config: RouteCalculatorConfig) => Promise<void>;
}

Vanilla Types

typescript
// RouteMap
interface RouteMapConfig extends MapConfig {
  onError?: (error: Error) => void;
}

class RouteMap {
  constructor(config: RouteMapConfig);
  initialize(element: HTMLElement): Promise<void>;
  destroy(): void;

  isReady(): boolean;
  getMap(): google.maps.Map | null;
  getCurrentRoute(): Route | null;

  renderRoute(route: Route): void;
  clearRoute(): void;

  setCenter(location: LatLng): void;
  getCenter(): LatLng | null;

  setZoom(zoom: number): void;
  getZoom(): number | null;

  fitBounds(bounds: MapBounds): void;
  getBounds(): MapBounds | null;

  addMarker(location: LatLng, options?: MarkerOptions): void;
  removeMarker(id: string): void;
  clearMarkers(): void;
}

// RouteOptimizer
interface RouteOptimizerEvents {
  'status-change': OptimizationStatus;
  progress: { progress: number; step: string };
  success: OptimizationResponse;
  error: Error;
  start: void;
  reset: void;
}

class RouteOptimizer {
  constructor(config: RouteCalculatorConfig);

  get status(): OptimizationStatus;
  get data(): OptimizationResponse | null;
  get error(): Error | null;
  get isLoading(): boolean;
  get isSuccess(): boolean;
  get isError(): boolean;

  initialize(): Promise<void>;
  optimize(request: OptimizationRequest): Promise<OptimizationResponse>;
  updateProgress(progress: number, step?: string): void;
  reset(): void;
  reinitialize(config: RouteCalculatorConfig): Promise<void>;
  destroy(): void;

  on<K extends keyof RouteOptimizerEvents>(
    event: K,
    listener: (data: RouteOptimizerEvents[K]) => void
  ): void;

  off<K extends keyof RouteOptimizerEvents>(
    event: K,
    listener: (data: RouteOptimizerEvents[K]) => void
  ): void;
}

Type Guards

Utility functions for type checking:

typescript
// Check if location is valid
function isValidLatLng(location: any): location is LatLng {
  return (
    typeof location === 'object' &&
    typeof location.lat === 'number' &&
    typeof location.lng === 'number' &&
    location.lat >= -90 &&
    location.lat <= 90 &&
    location.lng >= -180 &&
    location.lng <= 180
  );
}

// Check if stop is valid
function isValidStop(stop: any): stop is Stop {
  return typeof stop === 'object' && typeof stop.id === 'string' && isValidLatLng(stop.location);
}

// Check if route is valid
function isValidRoute(route: any): route is Route {
  return (
    typeof route === 'object' &&
    Array.isArray(route.stops) &&
    route.stops.length >= 2 &&
    route.stops.every(isValidStop)
  );
}

Usage:

typescript
function processRoute(data: unknown) {
  if (isValidRoute(data)) {
    // TypeScript knows data is Route
    routeMap.renderRoute(data);
  } else {
    console.error('Invalid route data');
  }
}

Generic Types

For advanced type safety:

typescript
// Generic Stop with custom metadata
interface Stop<T = Record<string, any>> {
  id: string;
  location: LatLng;
  name?: string;
  address?: string;
  duration?: number;
  metadata?: T;
}

// Usage
interface DeliveryMetadata {
  orderId: string;
  customerId: string;
  packageWeight: number;
  priority: 'low' | 'medium' | 'high';
}

const deliveryStop: Stop<DeliveryMetadata> = {
  id: 'delivery-001',
  location: { lat: 13.7563, lng: 100.5018 },
  metadata: {
    orderId: 'ORD-12345',
    customerId: 'CUST-001',
    packageWeight: 5.5,
    priority: 'high',
  },
};

Utility Types

typescript
// Partial route for updates
type PartialRoute = Partial<Route> & {
  stops: Stop[];
};

// Read-only route
type ReadonlyRoute = Readonly<Route> & {
  readonly stops: readonly Readonly<Stop>[];
};

// Pick specific stop properties
type StopLocation = Pick<Stop, 'id' | 'location'>;

// Omit metadata
type BasicStop = Omit<Stop, 'metadata'>;

// Required properties
type RequiredStop = Required<Stop>;

Type Imports

Import types from packages:

typescript
// From Core
import type {
  LatLng,
  Stop,
  Route,
  RouteOptions,
  MapConfig,
  MapBounds,
  OptimizationRequest,
  OptimizationResponse,
} from '@route-optimization/core';

// From React
import type {
  UseRouteMapOptions,
  UseRouteMapReturn,
  UseMapControlsReturn,
  UseRouteOptimizationResult,
} from '@route-optimization/react';

// From Vue
import type {
  UseRouteMapOptions,
  UseRouteMapReturn,
  UseMapControlsReturn,
  UseRouteOptimizationResult,
} from '@route-optimization/vue';

// From Vanilla
import type {
  RouteMapConfig,
  OptimizationStatus,
  RouteOptimizerEvents,
} from '@route-optimization/vanilla';

Best Practices

1. Use Strict Types

Enable strict TypeScript options:

json
// tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "strictNullChecks": true,
    "noImplicitAny": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true
  }
}

2. Type Assertions

Use type guards instead of assertions:

typescript
// ✅ Good - Type guard
if (isValidRoute(data)) {
  processRoute(data);
}

// ❌ Avoid - Type assertion
processRoute(data as Route);

3. Const Assertions

Use const assertions for literal types:

typescript
const mapConfig = {
  apiKey: 'YOUR_API_KEY',
  mapTypeId: 'roadmap',
  zoom: 12,
} as const;

4. Generic Constraints

Constrain generic types appropriately:

typescript
function getStopMetadata<T extends Record<string, any>>(
  stop: Stop<T>,
  key: keyof T
): T[keyof T] | undefined {
  return stop.metadata?.[key];
}

Next Steps

Released under the MIT License.