Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why boolean becomes true?

Tags:

typescript

Take a look at following code:

export class Smth {
  private flag: boolean;

  public update() {
    this.flag = true;

    this.inner();

    if (this.flag === false) { // Operator '===' cannot be applied to types 'true' and 'false'.
      console.log(123);
    }
  }

  private inner() {
    this.flag = false;
  }
}

I can't understand what's wrong with the line

if (this.flag === false)

Typescript says

Operator '===' cannot be applied to types 'true' and 'false'.

But actually there are boolean and false.

I'm using typescript 2.6.2 but online playground shows the same result with 2.7.


It's not a dublicate of Operator '==' cannot be applied to types x and y in Typescript 2 as that question is about comparing constants. But in my code it's a chagable class field and there is a function that changes the value. Moreover, it is called.

this.flag = true;

this.inner(); // exectues this.flag = false;

if (this.flag === false) { // ... types 'true' and 'false'. - WHY?
like image 559
Qwertiy Avatar asked Feb 05 '18 16:02

Qwertiy


2 Answers

TypeScript is kinda smart, in that it has a static analysis on your method, and it sees that you never assign anything other than false, at lease in this closure or context - which brings the type definition to assume your variable's type is false and not boolean. It does not look for changes in inner called methods.

Think of the definition as if it were declared globally like this:

export type boolean = true | false

while false is only false, without the true there.

There are several solutions:

  1. Assign the type from the get-go in the class declaration, like so:

    class MyClass {
      private flag: boolean = true
      ...
    }
    
  2. Just don't test for direct equality, use the boolean on its own:

    if (!this.flag) // instead of this.flag === false
    
like image 59
casraf Avatar answered Oct 10 '22 11:10

casraf


The problem you have is part of a broader discussion about the drawbacks of flow analysis. You can read the general problem here and a very similar problem to yours here. But the gist of it is:

The primary question is: When a function is invoked, what should we assume its side effects are? One option is to be pessimistic and reset all narrowings, assuming that any function might mutate any object it could possibly get its hands on. Another option is to be optimistic and assume the function doesn't modify any state. Both of these seem to be bad. This problem spans both locals (which might be subject to some "closed over or not" analysis) and object fields.

The simple way to work around this is to cast the constant to the general type. You should do this where necessary, one could argue the error has some value, as it warns you about possibly unreachable code and can be easily disabled where erroneous:

export class Smth {
  private flag: boolean;

  public update() {
    this.flag = true as boolean;

    this.inner();

    if (this.flag === false) { 
      console.log(123);
    }
  }

  private inner() {
    this.flag = false;
  }
}
like image 45
Titian Cernicova-Dragomir Avatar answered Oct 10 '22 11:10

Titian Cernicova-Dragomir