Flow: Disjoint unions with object maps are broken

Created on 10 Oct 2016  路  4Comments  路  Source: facebook/flow

This works:

// @flow

function foo(data: { [key: string]: string } | number): Array<string> {
  if (typeof data === "object") {
     return fooWithObject(data);
  }
  return ["1"];
}

function fooWithObject(data: { [key: string]: string }): Array<string> {
  return Object.keys(data).map(k => data[k]);
}

console.log(foo(1));
console.log(foo({"1": "show", "2": "me"}));

However, this is broken:

// @flow

function foo(data: { [key: string]: string } | number): Array<string> {
  if (typeof data === "object") {
     return Object.keys(data).map(k => (data: Object)[k]);
  }
  return ["1"];
}

console.log(foo(1));
console.log(foo({"1": "show", "2": "me"}));

with error:

5:      return Object.keys(data).map(k => (data: Object)[k]);
                                           ^ number. This type is incompatible with
5:      return Object.keys(data).map(k => (data: Object)[k]);
                                                 ^ object type

How can I help fix?

Most helpful comment

Maybe surprisingly another way to fix the error is to set the option experimental.const_params=true in .flowconfig (which prevents argument reassignments)

All 4 comments

It's not broken, Flow is pessimistic about refinements. In your case you might change the data reference, pointing to a number

function foo(data: { [key: string]: string } | number): Array<string> {
  if (typeof data === "object") {
    return Object.keys(data).map(k => {
      data = 1
      // return data[k]
      return 'evil code that typechecks'
    });
  }
  return ["1"];
}

A way to help Flow keep a refinement is to use a const binding (docs)

function foo(data: { [key: string]: string } | number): Array<string> {
  if (typeof data === "object") {
    const o = data // <= fix
    return Object.keys(o).map(k => o[k]);
  }
  return ["1"];
}

Maybe surprisingly another way to fix the error is to set the option experimental.const_params=true in .flowconfig (which prevents argument reassignments)

Thanks for the information! Apologies for filing an issue :)

In the first case can we have flow take note of where variables are reassigned in the AST to accurately track type information?

Haven't delved under the hood yet and was wondering whether, if all type cases have been checked, we can safely refine the types in these cases because there has been no reassignment?

Was this page helpful?
0 / 5 - 0 ratings