SolvedDefinitelyTyped [@types/react-router-dom]: withRouter HoC produces compile error: "Property 'match' is missing in type '{}'."

I did a Google search and found two other people with the same problem, but no solution so far:
https://stackoverflow.com/questions/44118060/react-router-dom-with-typescript
microsoft/vscode#27235

Minimal code example:

const MyRouterComponent = withRouter(
  class MyComponent extends React.Component<RouteComponentProps<any>, any> {
    render() {
      return <div />;
    }
  }
)

class MainComponent extends React.Component<any, any> {
  render() {
    /* This line produces following compile error:
       error TS2322: Type '{}' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<RouteComponentProps<any>, ComponentState...'.
          Type '{}' is not assignable to type 'Readonly<RouteComponentProps<any>>'.
            Property 'match' is missing in type '{}'.
    */
    return <MyRouterComponent />
  }
}

I understand what the problem is: MyRouterComponent expects props which I didn't explicitly pass, because withRouter will take care of that. So how does one use withRouter with TS correctly?

25 Answers

✔️Accepted Answer

Same issue here:

import React from 'react'
import { NavLink } from 'react-router-dom'
import { withRouter, RouteComponentProps } from 'react-router'
import { Project } from 'state'

interface Props {
  className: string
}

class SideBar extends React.Component<Props & RouteComponentProps<any>> {
  render() {
    const { className, match: { params: { project } } } = this.props
    return (
       <h1>{project}</h1>
    )
  }
}

export default withRouter(SideBar)
class Project extends React.Component<{}> {
  render() {
    return (
        <SideBar className="project__sidebar"/>
    )
  }
}

Got:

./src/components/Project.tsx
(14,18): error TS2322: Type '{ className: "project__sidebar"; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<SideBar> & Readonly<{ children?: ReactNode; }> & R...'.
  Type '{ className: "project__sidebar"; }' is not assignable to type 'Readonly<Props & RouteComponentProps<any>>'.
    Property 'match' is missing in type '{ className: "project__sidebar"; }'.

Can be fixed with:

export default withRouter<Props>(SideBar)

Bug with withRouter declaration, it can't extract real props.

Other Answers:

@simonvizzini You need to do something like:

const MyRouterComponent = withRouter<{}>(
  class MyComponent extends React.Component<RouteComponentProps<{}>, any> {
    render() {
      return <div />;
    }
  }
)

class MainComponent extends React.Component<any, any> {
  render() {
    return <MyRouterComponent />
  }
}

Since before, withRouter typing was:
export function withRouter(component: React.SFC<RouteComponentProps<any>> | React.ComponentClass<RouteComponentProps<any>>): React.ComponentClass<any>;

and now is:
export function withRouter<P>(component: React.SFC<RouteComponentProps<any> & P> | React.ComponentClass<RouteComponentProps<any> & P>): React.ComponentClass<P>;

@hafuta, are you suggesting that this has worked in previous versions? I'm trying to use withRouter for the first time, so I don't know. I'm using the latest versions of everything.

As a temporary workaround I figured out this:

return <MyRouterComponent { ...({} as RouteComponentProps<any>) } />

TS compiler is happy and withRouter injects the correct router props afterwards.

I will try to propose a PR today.

I wrote this module augmentation:

react-router-dom.d.ts

import { RouteComponentProps, withRouter } from 'react-router-dom'

declare module 'react-router-dom' {
  // Diff / Omit taken from https://github.com/Microsoft/TypeScript/issues/12215#issuecomment-311923766
  type Diff<T extends string, U extends string> = ({[P in T]: P } & {[P in U]: never } & { [x: string]: never })[T]
  type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>

  export function withRouter<P extends RouteComponentProps<any>>(
    component: React.ComponentType<P>
  ): React.ComponentClass<Omit<P, keyof RouteComponentProps<any>>>
}

It works for me. What do you think?

More Issues: