Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Configuration based React Router in TypeScript throwing eslint errors

Okay, as I look through the docs for React Router, I see that the suggestion is to use the new routers that support the standard Data History Web APIs, which are relatively new.

I’m in a browser, so I’m going to use createBrowserRouter.

There’s some sample code there in JavaScript. I need to port this to TypeScript for my project.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

import * as React from "react";
import * as ReactDOM from "react-dom";
import {
  createBrowserRouter,
  RouterProvider,
} from "react-router-dom";

import Root, { rootLoader } from "./routes/root";
import Team, { teamLoader } from "./routes/team";

const router = createBrowserRouter([
  {
    path: "/",
    element: <Root />,
    loader: rootLoader,
    children: [
      {
        path: "team",
        element: <Team />,
        loader: teamLoader,
      },
    ],
  },
]);

ReactDOM.createRoot(document.getElementById("root")).render(
  <RouterProvider router={router} />
);

When I do, I have errors.

I tried removing most of the stuff I don’t think I need, like the loaders, etc.

Here’s my first cut:

import React from 'react';
import ReactDOM from 'react-dom/client';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import type { BrowserRouter } from 'react-router-dom';
import AnItem from './components/Shared/AnItem';

const myRouter: BrowserRouter = createBrowserRouter([
    {
        path: '/',
        element: <AnItem />,
    },
]);

ReactDOM.createRoot(document.getElementById('root')!).render(
    <React.StrictMode>
        <RouterProvider router={myRouter} />
    </React.StrictMode>
);

There, I get a number of issues about any values, starting with the following line:

const myRouter: BrowserRouter = createBrowserRouter([

There are three errors reported just for that line (I’ll include them all in a screenshot, below):

  • Unsafe assignment of an any value.
  • ‘BrowserRouter’ refers to a value, but is being used as a type here. Did you mean ‘typeof BrowserRouter’?
  • Unsafe call of an any typed value.

errors as displayed in VS Code

What’s wrong there? I’ve typed myRouter. I get that the array is untyped for createBrowserRouter, but I thought it could infer.

But let’s pull it out and strongly type that array.

import React from 'react';
import ReactDOM from 'react-dom/client';
import type { RouteObject } from 'react-router';
import type { BrowserRouter } from 'react-router-dom';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import AnItem from './components/Shared/AnItem';

const routes: RouteObject[] = [
    {
        path: '/',
        element: <AnItem />,
    },
];

const myRouter: BrowserRouter = createBrowserRouter(routes);

ReactDOM.createRoot(document.getElementById('root')!).render(
    <React.StrictMode>
        <RouterProvider router={myRouter} />
    </React.StrictMode>
);

Now it’s happy through routes but still yells at me for…

const myRouter: BrowserRouter = createBrowserRouter(routes);

Unsafe assignment of an any value.

VS Code popover explaining the Unsafe assignment of an any value.

Where is the any? Is it from createBrowserRouter somehow? If I mouseover in VS Code, I get "intellisense" (if that’s still what it’s called), so I think my types, etc, are all mapping correctly:

(alias) createBrowserRouter(routes: RouteObject[], opts?: {
    basename?: string | undefined;
    hydrationData?: Partial<Pick<RouterState, "loaderData" | "actionData" | "errors">> | undefined;
    window?: Window | undefined;
} | undefined): Router
import createBrowserRouter

That says pretty clearly it returns a Router.

It looks like it can’t figure out what BrowserRouter is from that type BrowserRouter = /*unresolved*/ any. But when I mouseover the import, I get:

(alias) function BrowserRouter({ basename, children, window, }: BrowserRouterProps): JSX.Element
import BrowserRouter
A <Router> for use in web browsers. Provides the cleanest URLs.

'BrowserRouter' is declared but its value is never read.ts(6133)

So that seems in context too, though the "its value is never read" is surprising.

Now I did try to type myRouter as a Router, but I get yelled at that I should use a more specific type of router:

Note: You usually won’t render a directly [sic]. Instead, you’ll render a router that is more specific to your environment such as a in web browsers or a for server rendering.

So I’m using BrowserRouter. But its error is:

‘BrowserRouter’ refers to a value, but is being used as a type here. Did you mean ‘typeof BrowserRouter’?

I’m pretty sure I don’t mean typeof; I’m defining a variable as a specifc type.

What am I missing here? I’m having a hard time googling up an example that both uses configuration instead of components for routing (and I think configuration is what I want as the new Data History API routers require it) and is in TypeScript.

Looks like Drew’s on to something. Here are my dependencies from package.json:

    "dependencies": {
        "bootstrap": "^5.2.3",
        "react": "^18.2.0",
        "react-bootstrap": "^2.7.0",
        "react-dom": "^18.2.0",
        "react-router": "^6.7.0",
        "react-router-dom": "^6.7.0"
    },
    "devDependencies": {
        "@types/react": "^18.0.26",
        "@types/react-dom": "^18.0.9",
        "@typescript-eslint/eslint-plugin": "^5.48.2",
        "@typescript-eslint/parser": "^5.48.2",
        "@vitejs/plugin-react-swc": "^3.0.0",
        "eslint": "^8.32.0",
        "eslint-config-prettier": "^8.6.0",
        "eslint-config-xo": "^0.43.1",
        "eslint-config-xo-typescript": "^0.55.1",
        "eslint-plugin-react": "^7.32.1",
        "opener": "^1.5.2",
        "typescript": "^4.9.4",
        "vite": "^4.0.0"
    }

Update: If I disable those rules and take out the typing, things do render as I’d expect. 😖

/* eslint-disable @typescript-eslint/no-unsafe-assignment */
//...
const routes: RouteObject[] = [
    {
        path: '/',
        element: (
            <div>
                <a href="./test">test</a>
            </div>
        ),
    },
    { path: '/test', element: <div>hello</div> },
];

// eslint-disable-next-line @typescript-eslint/no-unsafe-call
const myRouter = createBrowserRouter(routes);

ReactDOM.createRoot(document.getElementById('root')!).render(
    <React.StrictMode>
        <RouterProvider router={myRouter} />
    </React.StrictMode>
);

>Solution :

react-router@6 and react-router-dom@6 are completely written in Typescript, so there is no need to import external typings. The v5 typings are likely messing with your build step.

You should uninstall @types/react-router and @types/react-router-dom.

npm uninstall --save @types/react-router
npm uninstall --save @types/react-router-dom

From here I don’t believe you’ll need to explicitly type the data router. The return type from createBrowserRouter is RemixRouter, not BrowserRouter.

import React from 'react';
import ReactDOM from 'react-dom/client';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import AnItem from './components/Shared/AnItem';

const myRouter = createBrowserRouter([
  {
    path: '/',
    element: <AnItem />,
  },
]);

ReactDOM.createRoot(document.getElementById('root')!)
  .render(
    <React.StrictMode>
      <RouterProvider router={myRouter} />
    </React.StrictMode>
  );
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading