microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.65k stars 12.44k forks source link

Type narrowing not working as expected in else #26852

Open Tyriar opened 6 years ago

Tyriar commented 6 years ago

For the following code with strictNullChecks enabled:

interface Base {
    a: {} | null;
    b: {} | null;
}

function test(base: Base): void {
    if (!base.a && !base.b) {
        const result: null = null;
    } else if (!base.a && base.b) {
        const result: {} = base.b;
    } else if (base.a && !base.b) {
        const result: {} = base.a;
    } else {
        const result: {} = base.b;
    }
}

I'm seeing this error on the last result:

Type '{} | null' is not assignable to type '{}'.
  Type 'null' is not assignable to type '{}'.

I expect it to work as the else implies:

   !(!base.a && !base.b) && !(!base.a && base.b) && !(base.a && !base.b) 
 = (base.a || base.b) && (base.a || !base.b) && (!base.a || base.b)
 = (base.a || base.b) && (base.a) && (base.b)
      note: !base.b and !base.a are canceled out as the expression cannot be true if one
            of them is false
 = base.a && base.b

TS play link

mattmccutchen commented 6 years ago

TypeScript narrowing doesn't do arbitrary Boolean algebra; it just looks for conjuncts of specific forms in the path condition of each path to the operation of interest. You could turn this issue into a suggestion, but I'm guessing it will be declined on performance grounds. Or you could just rewrite the code as:

function test(base: Base): void {
    if (!base.b) {
        if (!base.a) {
            const result: null = null;
        } else { 
            const result: {} = base.a;
        }
    } else { 
        const result: {} = base.b;
    }
}
Tyriar commented 6 years ago

@mattmccutchen makes sense, it's a suggestion/feature request then. It seems like something that could make type narrowing even more powerful than it is, I'm not sure what the performance challenges are though.

typescript-bot commented 5 years ago

This issue has been marked as 'Question' and has seen no recent activity. It has been automatically closed for house-keeping purposes. If you're still waiting on a response, questions are usually better suited to stackoverflow.

Tyriar commented 5 years ago

@DanielRosenwasser I think this is labelled incorrectly and shouldn't have been closed, it never really got an answer on why or why not from a maintainer.

DanielRosenwasser commented 5 years ago

Sorry, I don't know why this was labeled as a question.