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

React Router предоставляет гибкий механизм для организации маршрутов в приложениях на React, однако при работе с TypeScript и большими приложениями особенно важно правильно типизировать маршруты. Типизация маршрутов повышает безопасность кода, упрощает поддержку и минимизирует ошибки при навигации и передаче параметров.


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

Маршруты в React Router задаются с помощью компонентов <Routes> и <Route>. Для типизации маршрутов обычно создают централизованную структуру, где каждому маршруту соответствует ключ и параметры.

interface RouteParams {
  userId?: string;
  postId?: string;
}

interface AppRoutes {
  HOME: { path: "/" };
  USERS: { path: "/users" };
  USER_DETAILS: { path: "/users/:userId"; params: { userId: string } };
  POSTS: { path: "/posts" };
  POST_DETAILS: { path: "/posts/:postId"; params: { postId: string } };
}

const routes: AppRoutes = {
  HOME: { path: "/" },
  USERS: { path: "/users" },
  USER_DETAILS: { path: "/users/:userId", params: { userId: "" } },
  POSTS: { path: "/posts" },
  POST_DETAILS: { path: "/posts/:postId", params: { postId: "" } },
};

Ключевой момент: определение параметров маршрута через отдельное поле params позволяет TypeScript проверять наличие и типы этих параметров на этапе компиляции.


Использование useParams с типами

React Router предоставляет хук useParams для получения динамических сегментов URL. При типизации важно указывать интерфейс с ожидаемыми параметрами:

import { useParams } from "react-router-dom";

interface UserParams {
  userId: string;
}

function UserPage() {
  const { userId } = useParams<UserParams>();

  return <div>Пользователь с ID: {userId}</div>;
}

Типизация useParams<UserParams>() гарантирует, что TypeScript предупредит о попытках доступа к несуществующим или неправильно типизированным параметрам.


Для навигации используется компонент <Link> и хук useNavigate. При типизации маршрутов можно создать вспомогательную функцию для генерации URL с проверкой параметров:

import { generatePath } from "react-router-dom";

function buildPath<T extends keyof AppRoutes>(
  route: T,
  params?: AppRoutes[T] extends { params: infer P } ? P : never
) {
  return params ? generatePath(routes[route].path, params) : routes[route].path;
}

// Пример использования
<Link to={buildPath("USER_DETAILS", { userId: "123" })}>Профиль пользователя</Link>

Такой подход позволяет TypeScript проверять соответствие переданных параметров маршруту и предотвращает ошибки типа “не передан обязательный параметр”.


Комплексная типизация конфигурации маршрутов

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

interface RouteConfig<PathParams = undefined> {
  path: string;
  element: React.ReactNode;
  children?: RouteConfig[];
  params?: PathParams;
  requiresAuth?: boolean;
}

const routeConfig: RouteConfig[] = [
  { path: "/", element: <HomePage /> },
  { path: "/users", element: <UsersPage /> },
  { path: "/users/:userId", element: <UserPage />, params: { userId: "" }, requiresAuth: true },
];

Использование такой конфигурации позволяет строить маршруты динамически и типобезопасно:

function renderRoutes(routes: RouteConfig[]) {
  return routes.map(({ path, element, children }) => (
    <Route key={path} path={path} element={element}>
      {children && renderRoutes(children)}
    </Route>
  ));
}

TypeScript проверяет наличие необходимых полей и корректность параметров, снижая вероятность ошибок при масштабировании приложения.


Сопоставление маршрутов с параметрами в TypeScript

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

type RouteParams<T extends keyof AppRoutes> =
  AppRoutes[T] extends { params: infer P } ? P : never;

// Пример
const userPath: string = buildPath("USER_DETAILS", { userId: "42" }); // корректно
const invalidPath = buildPath("USER_DETAILS"); // ошибка компиляции

Такой подход делает маршруты полностью типобезопасными, гарантируя соответствие URL и передаваемых параметров.


Важные рекомендации

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

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