facebook / flow

Adds static typing to JavaScript to improve developer productivity and code quality.
https://flow.org/
MIT License
22.07k stars 1.86k forks source link

Function return type union not checked as strictly as separately-declared union? #5285

Open cakoose opened 6 years ago

cakoose commented 6 years ago
// @flow

declare function g1(): 'a' | 'b';
function f1(): number {
  const r = g1();
  if (r === 'a') return 1;
  if (r === 1) return 2;       // false negative (maybe)
  if (r === 'blah') return 3;  // false negative (inconsistent)
  return 0;
}

type Result = 'a' | 'b'
function f2(r: Result): number {
  if (r === 'a') return 1;
  if (r === 1) return 2;       // false negative (maybe)
  if (r === 'blah') return 3;  // true positive
  return 0;
}

flow.org/try output (link):

16:   if (r === 'blah') return 3;  // true positive
                ^ string literal `blah`. This type is incompatible with
13: function f2(r: Result): number {
                   ^ string enum

The "false negative (inconsistent)" is the main issue. I'm surprised that specifying a union in the function return type itself is different from specifying the union separately.

bradennapier commented 6 years ago

There have been a few bugs reported in the handling that was released with v0.58. That being said, it looks like it simply is expecting some extra refinement on your part.


If you look at the results it is not exact enough on the return (even though it is 'a' | 'b'). It is becoming 'a' | 'b' | string which is obviously not ideal but explains why the check for 'blah' is not causing any errors.

That being said, if you do specify the union instead directly on the var being checked it at least does report the false negative case for the string.

It does not, however, report the case for the number which appears to be a bug as well:

declare function g1(): *;

function f1(): number {
  const result: 'a' | 'b' = g1();
  if (result === 1) return 4
  if (result === 'blah') return 1
  if (result === 'a') return 2
  if (result === 'b') return 3
  return 0
}

Try Flow


8:   if (result === 'blah') return 1
                    ^ string literal `blah`. This type is incompatible with
6:   const result: 'a' | 'b' = g1();
                   ^ string enum