30

TypeScript Version: 2.0.2.0

Code I know the code is a bit stupid, but I actually have these kind of tests in my code (making an expression visitor) and I really think these should fly and compile right away.

var a: boolean = (true == false);
var b: boolean = (5 == 2);

Instead it complains that operand equal cannot be applied to types 'true', 'false', '5' and '2'. Mark that they are not boolean or number, they are actually a types of 'true','false','5','2'. I know that types 'string' and 'boolean' cannot be compared, but hey, 5 is actually a number, not type '5' or am I mistaken?

This compiles though.

let x = 2;
var a: boolean = 5 == x;
var b: boolean = <number>5 == <number>2;

Am I missing something, why arent't 5 and 2 considered as type 'number' ?

Expected behavior: Should compile

Actual behavior: Results in a compile error saying 'Operand '==' cannot be applied to types '<first argument>' and '<second argument>'

Background I came over this issues in typescript defining that it should be like this, but how come? https://github.com/Microsoft/TypeScript/issues/6167

Ahmad Baktash Hayeri
  • 5,802
  • 4
  • 30
  • 43
Lostfields
  • 1,364
  • 1
  • 12
  • 20

4 Answers4

11

why aren't 5 and 2 considered as type 'number'

The have the literal type 5 and 2. e.g.

var x: 5; 
// can only ever be assigned to 5 
x = 5; // okay 
x = 2; // Error 

I don't see an actual use case for wanting it not to be an error. This is just the compiler trying to help you. Feel free to create an issue if you see sufficient motivation

jonny
  • 3,022
  • 1
  • 17
  • 30
basarat
  • 261,912
  • 58
  • 460
  • 511
  • 1
    I can see that when you say that x should be type 5, but when I'm using 5 alone I thought it was type 'number'. – Lostfields Aug 31 '16 at 08:29
  • 1
    @Lostfields you think that `let x = 5` means `let x = 5 as number;` but it actually means `let x = 5 as 5`, crazy part with that that you can even write `let x = 5 as 3` at which point you are basically disabling typescript :) which is `let x = 5 as any` – JLarky Aug 30 '17 at 00:36
11

Literal types have many advantages, as it lets the compiler make types as narrow as possible. Your use case is one that comes up very rarely, but wanting types to be as narrow as possible permeates the entire design of the language. So yes, while it makes your life harder in this one specific case, it makes sense in the language as a whole. Users would have to suffer a significantly worse language, just to support this one rare use case.

Unfortunately, you will have to use the explicit typing you suggest yourself in the second example. I don't see this ever being fixed, because the majority of users wants the language to yell if they try to do this. It's probably the sign of a bug in a large majority of cases.

Simon Meskens
  • 928
  • 1
  • 6
  • 12
  • 2
    Can't say I agree with this. When I compare a variable to a literal (either strings or numbers, for example), I expect the result to be a boolean and for typescript not to "yell" at me and require me to write some crazy code to make it work. The following produces the same error, and I have to say it's stupid - if (key !== 'shippingMethodId' || key !== 'shippingAddress'). It's things like this that make me consider dropping Typescript altogether in favor of straight ES6. – Tim Hardy Mar 16 '18 at 19:57
  • I bet there's a very sane explanation of your issue too. I suggest you make a new question and link it here and I'll have a look at it. – Simon Meskens Apr 18 '18 at 06:34
6

As Erlang developer I used to see this kind of errors in Erlang, but was unsure what it means in TypeScript, here's example that will help you understand the problem:

let answer: "yes" | "no" | "maybe" = "yes";
if (Math.random() > 0.5) {
    answer = "maybe";
}

if (answer === "yes") {
    console.log('yes');
}

if (answer === "no") {
    console.log('no');
}

It will not compile with error:

error TS2365: Operator '===' cannot be applied to types '"yes" | "maybe"' and '"no"'.

First, here's the solution

let answer = "yes" as "yes" | "no" | "maybe";

Now the explanation:

Since this code is very simple and could be understood at compile time, TypeScript knows that there's no where in code where answer could become "no", so it just says to you (although in quite cryptic form) that answer is always not "no", so there's literally no reason to ever check if it is. But (as in Erlang) this can happen for quite obvious reason, when you for example decided to comment out some code for debug that was making answer to become "no". Now if we use let answer = "yes" as "yes" | "no" | "maybe"; or let answer = <("yes" | "no" | "maybe")>"yes"; it will make TypeScript think that "yes" can be "no" even if you can't see it in code. So for case of temporarily removed code there's second solution:

if (0) {
    answer = "no";
}

Even though this condition will never be true it is "complex" enough for TypeScript compiler to think that it can be true. My Erlang approach is to use when X and not X which would be if (x && !x) { but at least in 2.4 you can just use number expressions.

But at some point compiler might just be right and then solution is to remove check for "no" :)

So returning that back to the OP's question, to make your code compile you need to change it to:

var a = false;
var b = false;

If compiler knows that, you probably knew that too.

JLarky
  • 9,833
  • 5
  • 36
  • 37
0

Faced the same issue in a scenario as the following:

let a: string;

a === 'some-value1' && a === 'some-value2';  // <==

The second line produces the same error and maybe because Typescript is smart enough to know that a string type at a given moment cannot contain two (or more) different string literals.

The correct approach for the above expression would be to use OR in the expression:

a === 'some-value1' || a === 'some-value2';  // works fine :)
Ahmad Baktash Hayeri
  • 5,802
  • 4
  • 30
  • 43