Flow: React component's "All branches incompatible" (spurious?) only reported if instantiated

Created on 21 Feb 2019  Â·  2Comments  Â·  Source: facebook/flow

Flow version: 0.82.0 and later

Expected behavior

  1. Errors in a function definition should always appear, whether used or not.
  2. flow should report no errors with this function as input.

    Actual behavior

  3. Deleting the JSX block makes flow report everything is fine. Restoring the JSX block, which calls the function as a stateless React component, makes flow error, and point at the function definition, instead of at its usage.

  4. flow reports an error that all branches are incompatible with the return type.
// @flow
import React, { type Node } from 'react';
function RenderCond({children}: {children: Array<[boolean, ?Node]>}): ?Node {
  for (const [bool, Component] of children) {                      // ^ ERROR
    if (bool) {
      return Component;
    }
  }
  return null;
}

// Try deleting this block:
<RenderCond>
  {[
    [false, <h1>{'Comp 1'}</h1>],
    [true, <h1>{'Comp 2'}</h1>],
    [true, <h1>{'Comp 3'}</h1>],
  ]}
</RenderCond>

The above produces the following error on flow versions after 0.81.0:

$ flow --show-all-branches
Error ┈┈┈┈ src/util/renderCond.js:3:71

All branches are incompatible:
 • Either null or undefined [1] is incompatible with null [2].
 • Or null or undefined [1] is incompatible with boolean [3].
 • Or null or undefined [1] is incompatible with number [4].
 • Or null or undefined [1] is incompatible with string [5].
 • Or inexact null or undefined [1] is incompatible with exact React.Element [6].
 • Or null or undefined [1] is incompatible with React.Portal [7].
 • Or property @@iterator is missing in null or undefined [1] but exists in $Iterable [8].

     src/util/renderCond.js
      1│ // @flow
      2│ import React, { type Node } from 'react';
 [1]  3│ function RenderCond({children}: {children: Array<[boolean, ?Node]>}): ?Node {
      4│   for (const [bool, Component] of children) {                      // ^ ERROR
      5│     if (bool) {
      6│       return Component;

     /private/tmp/flow/flowlib_2060946e/react.js
 [2] 14│   | null
 [3] 15│   | boolean
 [4] 16│   | number
 [5] 17│   | string
 [6] 18│   | React$Element<any>
 [7] 19│   | React$Portal
 [8] 20│   | Iterable<?React$Node>;
bug

Most helpful comment

I believe Flow is correct here:

Without your usage in JSX, there's no way Flow could know your function is a React component. It could just be a general purpose function that happens to deal with React components as its inputs/outputs, in which case there's nothing wrong with it.

However, if you want it to be a React component, it needs to follow React standards. Which are that undefined is not a valid return type. You should just be using Node, which covers everything React can render, including null. By writing ?Node, you're making a union of Node | null | void (undefined).

Your Try Flow works if you replace all ?Node with Node.

All 2 comments

I believe Flow is correct here:

Without your usage in JSX, there's no way Flow could know your function is a React component. It could just be a general purpose function that happens to deal with React components as its inputs/outputs, in which case there's nothing wrong with it.

However, if you want it to be a React component, it needs to follow React standards. Which are that undefined is not a valid return type. You should just be using Node, which covers everything React can render, including null. By writing ?Node, you're making a union of Node | null | void (undefined).

Your Try Flow works if you replace all ?Node with Node.

@jamesisaac is spot on.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cubika picture cubika  Â·  3Comments

mjj2000 picture mjj2000  Â·  3Comments

tp picture tp  Â·  3Comments

ghost picture ghost  Â·  3Comments

philikon picture philikon  Â·  3Comments