Why TypeScript flow analysis doesn't cover else block?

Let’s consider the following code:

function f(x : number) {
    if (x === 1) {
        if (x === 2) {} // error
    else {
        if (x === 1) {} // OK

Here, the compiler gives me an error on x === 2.
The reason is simple: if execution has reached this block then x should be 1 as x === 1 passed.
And since 1 and 2 have no overlap it is impossible for x to be both 1 and 2.

But the compiler is totally OK with the second x === 1 inside the else.
This should not be possible as x has already failed check for x === 1 in the if statement.
And since x cannot satisfy both x === 1 and !(x === 1) the second if should give me the same error as for x === 2.

How is this possible?
Is this some kind of not implemented feature of advanced flow analysis? Is this a bug?
Or maybe this case have some hidden logic and in the end it does have some sense?


>Solution :

Because Typescript isn’t capable of expressing a not type.

When you do:

if (x === 1) { x } // x is type: 1

Then it knows x is 1 in that block. This is fine, 1 is valid and useful type. And it’s trivial to statically say that 1 === 2 can never be true.

But in the else block here:

if (x === 1) { x }
else { x } // x is type: number

Then x is type number, because the type system cannot express "all numbers except 1". So the compiler cannot actually guarantee that code path cannot be taken, even though you can see that.

It’s a limitation of the type system, sure. But Typescript isn’t perfect. It’s a type system on top of a different language, and this one of those things where you’re going have to settle for good enough.

Leave a Reply