Flow (0.58+) how to define children props when component is wrapped with withRouter

Viewed 486

Using Flow 0.58+, React and react-router-dom, what is the correct way of defining props children of a component that is wrapped with withRouter?

Currently, I have a strange behaviour that I would like to understand when trying to specifying the type of a component's children that is wrapped with withRouter.

Let's assume a RouterAware component:

import React, { type Node } from 'react';
import { withRouter } from 'react-router-dom';

type Props = {
  children: Node
};

const RouterAware = (props: Props) => <div>{props.children}</div>;

export default withRouter(RouterAware);

This component is then used like this:

const Root = () => (
  <RouterAware>
    <div>One</div>
    <div>Two</div>
  </RouterAware>
);

However, this gives 8 Flow errors, one for each of the 8 union types defined for React.Node:

declare type React$Node =
  | void
  | null
  | boolean
  | number
  | string
  | React$Element<any>
  | React$Portal
  | Iterable<React$Node>;

Error template is:

Error: src/js/Components/RouterAware.js:8
  8: const RouterAware = (props: Props) => <div>{props.children}</div>;
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function. This type is incompatible with
137:   | React$StatelessFunctionalComponent<Props>
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ object type. See lib: /private/tmp/flow/flowlib_116f8cac/react.js:137
  Callable property is incompatible:
      8: const RouterAware = (props: Props) => <div>{props.children}</div>;
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function. This type is incompatible with
    122:   (props: Props, context: any): React$Node,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function type. See lib: /private/tmp/flow/flowlib_116f8cac/react.js:122
      This parameter is incompatible:
          8: const RouterAware = (props: Props) => <div>{props.children}</div>;
                                         ^^^^^ object type. This type is incompatible with
        144:     Component: React$ComponentType<{| ...ContextRouter, ...P |}>
                                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ object type. See lib: ../../../flow-typed/npm/react-router-dom_v4.x.x.js:144
          Property `children` is incompatible:
              5:   children: Node
                             ^^^^ <Specific union type here>. This type is incompatible with
             35:   <RouterAware>
                   ^^^^^^^^^^^^^ React children array. See: src/js/Root.js:35

Those flow errors are fixable by changing the children: Node definition to children: Node[].

However, this seems wrong at first sight and might even be a problem with react-router-dom definition. I say this because without the withRouter wrapping call, the definition works as expected using simply children: Node, having one or multiple children.

To add to the confusion/problem, now if the component is called using a single child:

const Root = () => (
  <RouterAware>
    <div>One</div>
  </RouterAware>
);

The flow error is on children: Node[] with message div, The type is incompatible with object type array.

Is this the expected behaviour that when using withRouter, one should define its children props as being Node[] (even if it makes little sense since I would need children: Node | Node[] to handle both cases).

Or is this a bug? I think it's related to the how React components might turns children into either an array when multiple props.children=[<div>,<div>] or a single element when single props.children=<div>.

But even there, why it's only showing up with withRouter. Any explanation would be appreciate.

0 Answers
Related