@types/xxxx
package and had problems.Definitions by:
in index.d.ts
) so they can respond.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
https://github.com/Microsoft/vscode/issues/27235
Minimal code example:
````javascript
const MyRouterComponent = withRouter(
class MyComponent extends React.Component
render() {
return ;
}
}
)
class MainComponent extends React.Component
render() {
/* This line produces following compile error:
error TS2322: Type '{}' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes
Type '{}' is not assignable to type 'Readonly
Property 'match' is missing in type '{}'.
*/
return
}
}
````
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?
I'm having the same issue after upgrading react and related modules versions, following
@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:
javascript
return <MyRouterComponent { ...({} as RouteComponentProps<any>) } />
TS compiler is happy and withRouter
injects the correct router props afterwards.
@simonvizzini, yes, it worked for me before upgrading some packages. I'm using more or less the same workaround, I'm just passing nulls, the withRouter
function does inject the right values.
<DashboardMenu match={null} location={null} history={null} />
@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>;
@AlgusDark thank you very much! This indeed works. I'm now having a different problem, but I think I'm just doing it wrong. For example, this produces a compiler error:
````js
interface Props {
propA: string;
propB: number;
}
const MyRouterComponent = withRouter
class MyComponent extends React.Component
render() {
return ;
}
}
)
class MainComponent extends React.Component
render() {
return
}
}
````
Instead I have to do something like this:
js
class MyComponent extends React.Component<RouteComponentProps<{}> & Props, any> {
Looking at the type definition, it seems obvious why the first approach doesn't work, but is the second approach really the correct way? It doesn't seem to be very intuitive to me, but then again, I'm still learning TypeScripts type system so I'm probably just missing something.
@simonvizzini I'm too confused with this new way, because I feel that we can write this in a better way.
Maybe @mhegazy can help us with this, he did the changes at this commit.
Edit: Btw, Props
in RouteComponentProps<Props>
are the match.params
. So RouteComponentProps<{}> & Props
should be the correct way.
I've found a way to solve this problem as you can see below:
type OwnProps = RouteComponentProps<{id: number}>;
const mapStateToProps = (state: GlobalStateType, ownProps: OwnProps): StateToPropsType => ({
id: ownProps.match.params.id
});
type PropsType = StateToPropsType & DispathToPropsType & OwnProps;
class MyPage extends React.Component<PropsType, StateType> {
...
}
const mapDispatchToProps = (dispatch: DispatchType): DispathToPropsType => ({
...
});
export default withRouter(connect<StateToPropsType, DispathToPropsType, OwnProps> (
mapStateToProps,
mapDispatchToProps
)(MyPage));
In router:
<Provider store={store}>
<BrowserRouter>
<Switch>
<Route path="/:id(\d+)" component={MyPage} />
<Switch>
<BrowserRouter>
</Provider>
React Redux typings use a neat trick with Omit
mapped type. Maybe this pattern can be used here as well?
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?
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.
Use my module augmentation to extract props correctly.
https://github.com/DefinitelyTyped/DefinitelyTyped/issues/17181#issuecomment-339401310
We should do a PR with these modifications.
I will try to propose a PR today.
@Grmiade Any progress on this?
Sorry, I was overwhelmed these days, I will try this week
This PR https://github.com/DefinitelyTyped/DefinitelyTyped/pull/21329, in particular this line looks to have changed the meaning of the generic P
in withRouter<P>
. Previously, it meant the "own" properties expected by the Component returned by withRouter
; now, it means to also include the properties injected by withRouter
e.g. match
. Is my understanding of this change correct? Is this the intended API?
This change broke a large swath of our application as we depended on it meaning "own" properties. I'm trying to figure out if I should update the application to reflect this new meaning or make a new issue.
Thanks!
@jbmilgrom Your understanding is correct 👍
With this changes, it's not longer required to specify your "own" properties. It's inferred 🎉
I think you should update your application by removing your useless return type. You can found an example here.
According to me, specify his "own" properties expected by the component returned is a bad idea. It's too permissive and authorize the cheat :/ withRouter
's definition is the only one able to define the real return type. If you see what I mean ;) Thereby your typing becomes safe 💪 What do you think?
I think you're right, thanks for the quick response!
In our use cases, the consumers of the components returned by withRouter
care only about "own" props when trying to guarantee their correct use, which is accomplished both with and without this change, but I'm glad the new type now more accurately reflects whats going on.
This example: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react-router/test/WithRouter.tsx
is not working for me. The parent component that is trying to use this withRoutered component is being scolded that it hasn't supplied match
which is of course an internal prop.
"react-router-dom": "^4.2.2",
"@types/react-router-dom": "^4.2.2",
@crucialfelix Look this PR :)
https://github.com/DefinitelyTyped/DefinitelyTyped/pull/21799
I get this error on windows only.
Something this simple worked for me:
class BranchesList extends React.Component<
{ branches: BranchesModelType } & RouteComponentProps<{ page: number }>,
{}
> {
public render() {
// render something here
);
}
}
export default withRouter(BranchesList) as typeof BranchesList;
typeof is always such a life saver.
This issue is fixed I think. With https://github.com/DefinitelyTyped/DefinitelyTyped/pull/21799 and https://github.com/DefinitelyTyped/DefinitelyTyped/pull/21329. Can someone close it?
I have the same problem. but I took another method.
create new file in src/history.js
import createHistory from 'history/createBrowserHistory'
export default createHistory()
````
and change the index.tsx
````
import { Router } from 'react-router-dom';
ReactDOM.render(
<BrowserRouter history={history}>
<App />
</BrowserRouter>
, document.getElementById('root') as HTMLElement
);
````
and In the component, I can use the history.push("/path")
import history from '../../untils/history'
class ReviseCenter extends React.Component{
handleHistory () {
history.push('/home')
}
}
```
if I use withRouter it's wrong
Most helpful comment
Same issue here:
Got:
Can be fixed with:
Bug with
withRouter
declaration, it can't extract real props.