@types/react-router
package and had problems.Definitions by:
in index.d.ts
) so they can respond.Currently, withRouter
is having this definition:
export function withRouter<P>(component: React.ComponentType<RouteComponentProps<any> & P>): React.ComponentClass<P>;
However, when using with connect
of react-redux
like mentioned in the documentation the parameter doesn't match. Here is the exception:
Argument of type 'ComponentClass<{}>' is not assignable to parameter of type 'ComponentType<RouteComponentProps<any>>'.
Type 'ComponentClass<{}>' is not assignable to type 'StatelessComponent<RouteComponentProps<any>>'.
Type 'ComponentClass<{}>' provides no match for the signature '(props: RouteComponentProps<any> & { children?: ReactNode; }, context?: any): ReactElement<any> | null'.
This happen using exactly:
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(PageContent));
A workaround is to cast with any:
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(PageContent) as any)
connect
doesn't return React.ComponentType<RouteComponentProps<any> & P>
but React.ComponentClass<{}>
The same problem too, but thanks for the workaround !
Thank you for logging this, I ended up reverting to 3.0.12 (at least until this gets fixed).
@MrDesjardins Is this still a problem? I can make a test case in the tests with the following and I don't get that error. I get a different error about <Test />
needing all the properties listed in RouterComponentProps
(location, match, history).
namespace WithRouter {
class TestComponent extends Component<RouteComponentProps<any>> {}
const mapStateToProps = () => ({});
const mapDispatchToProps = () => ({});
const Test = withRouter(connect(mapStateToProps, mapDispatchToProps)(TestComponent)); // this is fine
const verify = <Test />; // requires `location`, `match` and `history`. I don't know if that is expected
}
@NicholasBoll I still need to do the workaround I proposed to move forward with the library but I haven't updated anything since. Here is a screenshot of the actual problem.
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>
This seems to extend to the Route component as well, when I try to do
const authenticatedRedir = connectedRouterRedirect({...})
<Route path="/" component={authenticatedRedir(connectedApp)} />
I'm getting a similar Argument of type 'ComponentClass<...>' is not assignable to parameter of type 'ComponentType<...>'
which is also fixed by casting to any
. Thanks for the workaround!
I could avoid any
by using RouteComponentProps
which @NicholasBoll said above though I don't completely understand this mechanism.
interface MyProps extends RouteComponentProps<MyProps> {}
class TestComponent extends React.Component<MyProps, {}> {}
const mapStateToProps = () => ({});
const mapDispatchToProps = () => ({});
const Test = withRouter(connect(mapStateToProps, mapDispatchToProps)(TestComponent));
Issues very similar to mine:
https://stackoverflow.com/questions/48260971/react-ts-withrouter-connect-error
https://stackoverflow.com/questions/48219432/react-router-typescript-errors-on-withrouter-after-updating-version
Issues somehow related to mine, as far as I can tell:
https://github.com/DefinitelyTyped/DefinitelyTyped/pull/21329
https://github.com/DefinitelyTyped/DefinitelyTyped/issues/17355
package-lock.json:
"@types/react-router": {
"version": "4.0.22",
I tried many different types for the Component props, like the one in the post above this one. None of that worked.
Component code:
class HeaderTabs extends React.Component<RouteComponentProps<any>, any> {}
const mapStateToProps = () => ({})
const mapDispatchToProps = () => ({})
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(HeaderTabs));
The error I have is:
Argument of type 'ComponentClass<Pick<any, never>> & { WrappedComponent: ComponentType<any>; }' is not assignable to parameter of type 'ComponentType<RouteComponentProps<any>>'.
Type 'ComponentClass<Pick<any, never>> & { WrappedComponent: ComponentType<any>; }' is not assignable to type 'StatelessComponent<RouteComponentProps<any>>'.
Type 'ComponentClass<Pick<any, never>> & { WrappedComponent: ComponentType<any>; }' provides no match for the signature '(props: RouteComponentProps<any> & { children?: ReactNode; }, context?: any): ReactElement<any> | null'.
The only workaround was to use:
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(HeaderTabs) as any);
Please advice
Just a heads up - you shouldn't be passing in component props as the
For example, if you expect two params (blogName and blogId) and a few props (logo and items) on this Route, then your full component props would look like:
interface IProps extends RouteComponentProps<{ blogName: string, blogId: number }> {
className?: string
logo?: React.ReactNode
items?: Array<INavItem>
}
Note in the following image how VSCode is correctly picking up my params:
After some investigation. I found a simple solution. What you only have to do is to have "compilerOptions": { "strict": true } in your tsconfig.json if you're connecting a React.Component<,> component.
Thanks for this workaround @MrDesjardins ! Got badly stuck with router/redux with typescript integration and this saved me some hours.
@guilhermehubner and @emag's posts gave me an idea on how the withRouter mechanism works.
I find that you don't even have to extend the RouteComponentProps, if you pass a compatible props to the withRouter, it will automatically extends the RouteComponentProps.
Here's how I got it to work:
In the Component:
type Props = {
router: any; // Make sure you include router props here
createZone?: any;
onInitialized?: any;
};
type State = {
message: string;
loading: boolean;
};
class InitializeScreen extends React.PureComponent<Props, State> {
// Component stuff here
}
// Finally
export default withRouter<Props>(
connect(
mapStateToProps,
mapDispatchToProps
)(InitializeScreen)
);
To use the component
//Somewhere in your code:
<InitializeScreen onInitialized={onInitialized} />
Here's an example of how to use together within a functional component
interface MyProps extends RouteComponentProps {
otherProp: string;
}
const UnconnectedPrivateRoute = ({
otherProp,
history
}: MyProps) => (
.........
)
const mapStateToProps = (state: RootState) => ({
username: state.user.attributes?.userName,
attributes: state.user.attributes,
isLoggedIn: state.user.isLoggedIn
});
const mapDispatchToProps = { logout };
export const PrivateRoute = withRouter(
connect(mapStateToProps, mapDispatchToProps)(UnconnectedPrivateRoute)
);
@TomYeoman Or just use the hooks: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/hooks.md
Most helpful comment
I could avoid
any
by usingRouteComponentProps
which @NicholasBoll said above though I don't completely understand this mechanism.