Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React-router + typescript type error

While creating my react app with typescript I ran into a small issue that I have not been able to solve just yet.

My code:

App.tsx

import  * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import * as promise from 'redux-promise';
import reducers from './reducers';
import TemplateNavTop from './components/layout/template-nav-top';
const TestComponent2 = () => {
  return <h1>TestComponent</h1>;
}

const createStoreWithMiddleware = applyMiddleware(promise)(createStore);

ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
    <BrowserRouter>
        <Switch>
            <Route path="/" exact component={TestComponent} />
            <Route path="/checkout">
                <TemplateNavTop>
                    <TestComponent2 />
                </TemplateNavTop>
            </Route>
        </Switch>
    </BrowserRouter>
</Provider>
, document.getElementById('root')

template-nav-top

import * as React from 'react';
import NavTop from './nav-top/nav-top';
export default class TemplateNavTop extends React.Component<any, {}> {
  render() {
    return (
        <div>
            asd
            {this.props.children}
            Footer
        </div>
    );
  }
}

The problem appears in the /checkout route, where it complains about the following:

Type '{ path: "/checkout"; children: Element; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<Route> & Readonly<{ children?: ReactNode; }> & Rea...'.
  Type '{ path: "/checkout"; children: Element; }' is not assignable to type 'Readonly<RouteProps>'.
    Types of property 'children' are incompatible.
      Type 'Element' is not assignable to type '(props: RouteComponentProps<any>) => ReactNode'.

I found out the the following workaround does work:

<Route path="/checkout" component={() => TemplateWithNavBar(<TestComponent2 />)} />

But I'd rather do it the right way, anyone here able to help me out?

Edit: I Do have the @types installed

like image 397
Sebastiaan van Arkens Avatar asked May 29 '17 21:05

Sebastiaan van Arkens


1 Answers

Edit and TL;DR: Just upgrade your type declarations with

npm install --save-dev @types/react-router

Explanation

This is a bug in the declaration files. The problem is was that the type of children was originally expected to be

((props: RouteComponentProps<any>) => React.ReactNode | React.ReactNode)

which really is a function that returns a union of React.ReactNode | React.ReactNode which collapses down to a simple React.ReactNode.

What it really should have been was

((props: RouteComponentProps<any>) => React.ReactNode) | React.ReactNode

I've opened up a pull request for you here.

I should mention that you may not want to take a dependency on this behavior. Almost all examples I can find online use an explicit attribute for children while passing in a function with react-router. Even the documentation itself says that it only takes a function (rather than an element or anything else).

like image 91
Daniel Rosenwasser Avatar answered Oct 13 '22 15:10

Daniel Rosenwasser