9

The expression 1==2 causes TypeScript to give the error Operator '==' cannot be applied to types '1' and '2'. What are the reasons for TypeScript treating these values as being of a different type (the typeof operator, rather predictably, says that they're both numbers)? Is it a design decision to specifically apply this to numbers, or a byproduct of the overall typing system? What harm could be caused by allowing comparison of literals?

8
  • 1
    thats just because the numbers have different values, it's not a type problem Commented Aug 31, 2017 at 11:29
  • 3
    But then surely the expression would just return false, rather than causing the script to not compile? Commented Aug 31, 2017 at 11:33
  • Well, it does compile, just with a warning… but yes, interesting question. Here's a link to reproduce: typescriptlang.org/play/#src=1%20%3D%3D%202%3B Commented Aug 31, 2017 at 11:33
  • 2
    @ZevSpitz "Why" is not really relevant to the question at hand. I'm asking about the design decision. Commented Aug 31, 2017 at 11:35
  • 2
    @deceze there are no warnings, that is an error, but the process will still emit the js, unless the --noEmitOnError flag is used Commented Aug 31, 2017 at 11:36

3 Answers 3

7

1 and 2 in this context are considered so called literal numbers. It means that value 1 has type 1 and thus can only be 1, same with 2. Given that, the expression of 1==2 doesn't make sense because 1 can never be 2, or more precisely their types mismatch, you can't compare apples to oranges.

Here are the rationale and in depth details on where literals types are assumed by default:

About literal types:

One of many examples as to why literal types are useful:

Sign up to request clarification or add additional context in comments.

5 Comments

A bunch of singleton types with just one inhabitant aren't that useful. Why isn't a number literal just a polymorphic Number type like in untyped Javascript? This is a step back and quite disappointing. People shouldn't introduce a rigid, static type system without providing concepts to make it more flexible again.
there is no clear vision as to what is more beneficial: for some scenarios literal types work better for other scenarios they bring problems, here is more about it: github.com/Microsoft/TypeScript/pull/10676
Regarding your Why literal types are useful link: The tags of tagged unions don't have to be types themselves. They can just be kind of like constants. Haskell, ML, Swift, and Rust all take this approach and I am sure they do so for a good reason.
rephrased to avoid falling into conclusion that this is the sole purpose of literal types
@ftor when it comes to sum types TypeScript is way ahead of Haskell and ML simply because it delegates to you the problem of discriminating Just from Nothing, and if you are smart enough you can have a type that looks like Just a | Right a without making runtime choke
5

When TypeScript peforms type inference on the expression 1, it gives it the type 1, not the type number. You can see this if you examine code like this:

const a = 1;

If you use your IDE to query the inferred type of a, you'll see that the type of a is 1. In the TypeScript playground, for instance, you get a tooltip that says const a: 1.

So in if (1 == 2), 1 has type 1 and 2 has type 2. TypeScript does not allow you to compare them because they are of different inferred types. This is part of the type safety TypeScript gives you.

You can work around it with:

if (1 as number == 2) {
}

And you mentioned in a comment that you were doing the 1 == 2 comparison because you could not do if (false) { ... } due to the compiler complaining about unreachable code. I can work around that problem with this:

if (false as boolean) {
    console.log("something");
}

1 Comment

What additional type safety is gained when 1 == 2 isn't compilable? That this concept comes at a price is apparent from your example false as boolean.
4

Typescript can create a type from any constant value. This when combined with union types creates a very powerful way of expressing what a function takes as an argument for example:

function doStuff(p : "yes"| 1| true| "no"| 0| false ){

}

doStuff("maybe"); //Error
doStuff(3); // Error
doStuff(1) ; //OK

You are experiencing the unfortunate side effect that errors such as yours instead of being reports as expression is always false turn into type compatibility errors instead.

7 Comments

Treating each different number literal as a singleton type isn't powerful. It's odd and limiting.
It is limiting where you want it to be limiting, Typescript is all about imposing limits on what you can write making sure that what you write is valid. 1 == 2 is probably an error, passing a function an argument it does not expect such as the example above is an error. Typescript want to catch as many of these at compile time instead of runtime
I know the purpose of a static type system. But apparently TypeScript doesn't know polymorphism.
Both types 1 and 2 are assignable to number (the base type). This is polymorphism. In C# for example you can't assign a Triangle to a Square even if they both inherit Shape, as expected. It is the same principle. I am curious where you see a problem with this implementation.
Literal type singletons add complexity without providing reasonable benefits. If I have a = true; b = true it doesn't give me anything if the types are literally bound to their values. It is just a matter of value equality and the type system should merely protect me from comparing different types.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.