Flow: Un-reachable code in disjoint union case analysis should trigger an error

Created on 26 Jul 2016  路  5Comments  路  Source: facebook/flow

Consider the following definitions for a disjoint union.

type Type1 = { tag: 'T1', value: string };
type Type2 = { tag: 'T2' };
type Type = Type1 | Type2;

and the function

const fn = (x: Type): string => {
    if (x.tag === 'T0') {
        return x.value;
    } else if (x.tag === 'T2') {
        return 'default';
    } else {
        return 'unexpected';
    }
}

Notice that in the first branch we're mistakingly using T0 instead of T1. This code will compile just fine.

I would expect flow to either

  • raise an error telling me x.value in the first return leads to an invalid property access
  • raise an error telling me the first return is an un-reachable code path because x.tag === 'T3' can never be true.

Either errors should lead to the realization that I've typo-ed the tag name in the first if.

Similarly, the following function which should compile fine raises a compilation error:

const fn = (x: Type): string => {
    if (x.tag === 'T1') {
        return x.value;
    } else if (x.tag === 'T2') {
        return 'default';
    }
}

Error:

const fn = (x: Type): string => {
                      ^^^^^^ string. This type is incompatible with an implicitly-returned undefined.

To fix this requires adding an additional else where we need to return some bogus value just to make the compiler happy:

const fn = (x: Type): string => {
    if (x.tag === 'T1') {
        return x.value;
    } else if (x.tag === 'T2') {
        return 'default';
    } else {
        return 'unexpected';
    }
}

This error also occurs when using switch statements instead of ifs.

Most helpful comment

This will be fixed soonish.

All 5 comments

I don't have a great answer, but there is a known hacky workaround to get exhaustiveness checking, described here: http://www.adamsolove.com/js/flow/type/2016/04/15/flow-exhaustiveness.html

This will be fixed soonish.

We use another workaround to check the exhaustiveness of the switch: http://ouicar.github.io/2016/08/08/exhaustive-switch.html The idea is to add an empty type to make sure that the destructured value is of type any.

This should be fixed in the latest version of Flow. If you are still having a problem with this please reopen with a reproduction on https://flow.org/try 馃槉

The example listed above seems to still have the problem on v51.1, which is the latest on try flow: https://flow.org/try/#0C4TwDgpgBAKuEEYoF4oG8rAIYHMBcUA5DAoQDRQBuWANgK4QEDOwATgJYB2OUAvgNwAoUJFjwATCnSZcBYuMJ8hI6HFGo1iKAB8xkcUMEBjAPacWUAGacpACgAeBTQEpmbLj2QA+dIKj+odksoBwA6bE9kVGIABkJnXwCkqFYIYDpWG3tQ6noIISTeKAgaJmggkOyIlCiiGAUEtD9k-1T0zKIAEwhLLDoaYEICgKKSssSWlLSMm0I6Tgh7SCNgCE6h5v9eQW2gA

Note that in this case, "has the problem" means that that code compiles fine, when OP thinks it should fail.

Was this page helpful?
0 / 5 - 0 ratings