Типизация параметров

Universal Router — это гибкая библиотека маршрутизации для JavaScript, которая позволяет определять маршруты с динамическими сегментами, передавать параметры через URL и обрабатывать их в едином объекте контекста. Одним из ключевых аспектов работы с динамическими маршрутами является корректная типизация параметров, чтобы приложение получало значения нужного типа без дополнительных преобразований.


Определение параметров маршрута

В Universal Router параметры маршрута задаются через сегменты с двоеточием в пути:

const routes = [
  {
    path: '/user/:id',
    action: ({ params }) => {
      return `User ID: ${params.id}`;
    }
  }
];

Здесь :id — это динамический параметр. В объекте params он всегда приходит в виде строки. Для большинства приложений этого может быть недостаточно, если требуется числовой идентификатор, булево значение или дата.


Преобразование типов через action

Для обеспечения строгой типизации параметров можно использовать ручное преобразование внутри action:

{
  path: '/product/:productId',
  action: ({ params }) => {
    const productId = Number(params.productId);
    if (isNaN(productId)) throw new Error('Invalid product ID');
    return `Product ID: ${productId}`;
  }
}

Особенности:

  • Преобразование типов выполняется в момент вызова action.
  • Можно проверять корректность данных и выбрасывать ошибки при неверном типе.
  • Все параметры приходят как строки, даже если в URL указаны цифры.

Валидация и сопоставление через регулярные выражения

Universal Router позволяет задавать регулярные выражения для сегментов, чтобы сразу фильтровать значения нужного формата:

{
  path: '/order/:orderId(\\d+)',
  action: ({ params }) => {
    const orderId = parseInt(params.orderId, 10);
    return `Order ID: ${orderId}`;
  }
}

Здесь \\d+ гарантирует, что маршрут сработает только для цифровых значений orderId.

Преимущества:

  • Типизация осуществляется на уровне маршрута.
  • Невалидные значения автоматически исключаются из сопоставления.
  • Сокращает необходимость дополнительных проверок в action.

Использование TypeScript для строгой типизации

При интеграции с TypeScript можно определить интерфейсы параметров для маршрутов:

interface UserParams {
  id: number;
}

const routes: Route<UserParams>[] = [
  {
    path: '/user/:id(\\d+)',
    action: ({ params }) => {
      const id = Number(params.id);
      return `User ID: ${id}`;
    }
  }
];

Преимущества подхода:

  • IDE сразу подсказывает тип параметра.
  • Ошибки преобразования можно отловить на этапе компиляции.
  • Повышает читаемость и поддержку кода при больших приложениях.

Комплексная типизация нескольких параметров

Если маршрут содержит несколько параметров, их типизация также выполняется через интерфейсы:

interface OrderParams {
  userId: number;
  orderId: number;
}

const routes: Route<OrderParams>[] = [
  {
    path: '/user/:userId(\\d+)/order/:orderId(\\d+)',
    action: ({ params }) => {
      return `User ${params.userId} - Order ${params.orderId}`;
    }
  }
];

Рекомендации:

  • Применять регулярные выражения к каждому сегменту, чтобы сразу фильтровать некорректные данные.
  • Преобразовывать параметры к нужному типу внутри action или через вспомогательные функции.
  • Использовать TypeScript для явного указания типов и предотвращения ошибок при дальнейшей обработке.

Встроенные функции преобразования

Можно создавать универсальные функции для преобразования параметров:

function parseNumberParam(value) {
  const number = Number(value);
  if (isNaN(number)) throw new Error('Parameter is not a number');
  return number;
}

const routes = [
  {
    path: '/item/:itemId(\\d+)',
    action: ({ params }) => {
      const itemId = parseNumberParam(params.itemId);
      return `Item ID: ${itemId}`;
    }
  }
];

Преимущества такого подхода:

  • Упрощает повторное использование логики преобразования.
  • Централизованная обработка ошибок.
  • Облегчает поддержку типизации в больших проектах с множеством маршрутов.

Поддержка сложных типов

Параметры маршрута могут представлять собой сложные структуры, например массивы или JSON, передаваемые через query-параметры:

{
  path: '/search',
  action: ({ query }) => {
    const filters = query.filters ? JSON.parse(query.filters) : [];
    return `Filters count: ${filters.length}`;
  }
}

Особенности:

  • В URL массивы или объекты передаются в виде строки.
  • Необходима ручная десериализация и проверка типов.
  • TypeScript интерфейсы помогают структурировать ожидаемые данные.

Практические рекомендации

  1. Использовать регулярные выражения в пути для базовой фильтрации типов.
  2. Преобразовывать параметры внутри action, чтобы гарантировать корректный тип.
  3. Интегрировать TypeScript для явной типизации всех параметров маршрута.
  4. Создавать вспомогательные функции для часто используемых преобразований и проверок.
  5. При работе с query-параметрами и сложными структурами применять парсинг и валидацию для предотвращения ошибок.

Эти подходы позволяют строить маршруты в Universal Router с чёткой типизацией, безопасной обработкой данных и предсказуемым поведением приложения даже при сложных динамических URL.