Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flow doesn't recognise refinement inside callback

Tags:

flowtype

This code passes the flow check:

/* @flow */

function test (list: ?Array<string>): Promise<number> {
  if(list !== null && list !== undefined) {
    return Promise.resolve(list.length)
  } else {
    return Promise.resolve(0)
  }
}

console.log(test(null))

Whereas the following gets a null check error

/* @flow */

function test (list: ?Array<string>): Promise<number> {
  if(list !== null && list !== undefined) {
    return Promise.resolve().then(() => list.length)
  } else {
    return Promise.resolve(0)
  }
}

console.log(test(null))

error:

property `length`. Property cannot be accessed on possibly null value

Clearly list cannot be null so there must be something about the code structure that makes flow unable to recognise this.

I would like to understand what limitation I am hitting and how I can work around it. Thanks!

like image 999
sorenbs Avatar asked Mar 13 '23 16:03

sorenbs


1 Answers

Basically, Flow doesn't know that your type refinement (null check) will hold at the time when () => list.length is executed. Inside that callback Flow only looks at the type of list – which says it can be null.

The difference between first and second snippet is that in the second snippet list is crossing a function boundary – you're using it in a different function than where you refined its type.

One solution is to extract list.length into a variable, and use that variable in the callback.

var length = list.length;
return Promise.resolve().then(() => length)

This might also work:

var list2: Array<string> = list;
return Promise.resolve().then(() => list2.length)

Note that this problem exists even for immediately invoked callbacks, e.g. when using map or forEach. There is an issue on flow's github about this, but I couldn't find it after a quick search.

like image 160
Nikita Avatar answered Mar 20 '23 09:03

Nikita