Типобезопасные параметры

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


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

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

import { BrowserRouter, Routes, Route } from "react-router-dom";
import UserPage from "./UserPage";

<BrowserRouter>
  <Routes>
    <Route path="/user/:userId" element={<UserPage />} />
  </Routes>
</BrowserRouter>

В этом примере :userId является параметром маршрута. Без TypeScript тип параметра неизвестен и воспринимается как string | undefined. Для строгой типизации необходимо использовать типы для этих параметров.


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

Хук useParams позволяет получить параметры маршрута в компоненте:

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

interface UserParams {
  userId: string;
}

const UserPage: React.FC = () => {
  const { userId } = useParams<UserParams>();
  return <div>Пользователь: {userId}</div>;
};

Здесь интерфейс UserParams определяет ожидаемые параметры. TypeScript проверяет доступ к userId и предотвращает ошибки, связанные с опечатками или отсутствием поля.


Обязательные и необязательные параметры

Параметры маршрута могут быть обязательными и необязательными:

<Route path="/product/:productId/:tab?" element={<ProductPage />} />
  • productId — обязательный параметр.
  • tab — необязательный параметр (символ ?).

Соответствующий интерфейс:

interface ProductParams {
  productId: string;
  tab?: string;
}

Использование useParams<ProductParams>() позволяет корректно обработать оба типа параметров.


Преобразование параметров в числа и другие типы

Все параметры, получаемые через URL, имеют тип string. Часто требуется конвертировать их в другие типы:

interface ItemParams {
  itemId: string;
}

const ItemPage: React.FC = () => {
  const { itemId } = useParams<ItemParams>();
  const numericId = Number(itemId);

  if (isNaN(numericId)) {
    return <div>Некорректный идентификатор</div>;
  }

  return <div>Элемент №{numericId}</div>;
};

Типизация через интерфейс гарантирует, что itemId всегда присутствует как строка, а конвертация и проверка числа предотвращает ошибки на ранней стадии.


Типобезопасная навигация

При навигации через useNavigate также важно учитывать типы параметров:

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

const navigate = useNavigate();

const goToUser = (id: number) => {
  navigate(`/user/${id}`);
};

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


Пример сложной структуры маршрутов с параметрами

<Routes>
  <Route path="/users/:userId" element={<UserPage />} />
  <Route path="/users/:userId/posts/:postId" element={<PostPage />} />
</Routes>

Интерфейсы параметров для вложенных маршрутов:

interface PostParams {
  userId: string;
  postId: string;
}

const PostPage: React.FC = () => {
  const { userId, postId } = useParams<PostParams>();
  return (
    <div>
      Пользователь: {userId}, Пост: {postId}
    </div>
  );
};

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


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

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

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

interface AppRoutes {
  user: { userId: string };
  post: { userId: string; postId: string };
}

const routes: RouteObject[] = [
  {
    path: "/user/:userId",
    element: <UserPage />,
  },
  {
    path: "/user/:userId/post/:postId",
    element: <PostPage />,
  },
];

Типы параметров могут быть использованы в комбинации с вспомогательными функциями генерации URL, что позволяет навигировать по приложению строго типизированным образом:

function buildUserUrl(params: AppRoutes["user"]) {
  return `/user/${params.userId}`;
}

function buildPostUrl(params: AppRoutes["post"]) {
  return `/user/${params.userId}/post/${params.postId}`;
}

Основные преимущества типобезопасных параметров

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

Типобезопасность в React Router достигается через комбинацию TypeScript-интерфейсов, хука useParams, строгой конверсии типов и типизированной генерации URL, что делает работу с динамическими маршрутами безопасной и предсказуемой.