Typescript: 'bigint' is not comparable to 'number' with loose equality

Created on 22 Mar 2019  ·  7Comments  ·  Source: microsoft/TypeScript


TypeScript Version: 3.3.3


Search Terms: bigin == number not comparable loose equality

Code

1n == 1

error:

This condition will always return 'false' since the types 'bigint' and 'number' have no overlap.

but in chrome 72.0.3626.121

> 1n == 1
< true

and in MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt
QQ截图20190322171720

And similar issues

let x = [1, 2, 3]
x[1n]

error:

Type '1n' cannot be used as an index type.

in Chrome

>  let x = [1, 2, 3]
   x[1n]
<  2

Playground Link:
http://www.typescriptlang.org/play/index.html#src=1n%20%3D%3D%201
http://www.typescriptlang.org/play/index.html#src=let%20x%20%3D%20%5B1%2C%202%2C%203%5D%0D%0Ax%5B1n%5D

Related Issues:
https://github.com/Microsoft/TypeScript/issues/30655

Awaiting More Feedback Suggestion

Most helpful comment

TS may rightly want to error on people using == at all. But... if someone uses == instead of ===, then TS should accurately make its assumptions about what == will do. It appears from this thread (and others) that's not the case. Wanting to throw useful errors is fine, but incorrectly representing how JS works is not.

IOW, the vastly more common perspective (much to my chagrin) is that devs default to using ===, and that's ESPECIALLY true for those who use type systems like TS. So, if someone is going out of their way to use TS, and they choose a ==, and that's allowed by their linter/settings, then the assumption should be they intended to do that, and that they intended to take advantage of the coercive capabilities of ==.

All 7 comments

I agree that the error message This condition will always return 'false' is not strictly correct. But comparing number and bigint values for equality should definitely cause a type-checking error. It is not obvious how to compare a bigint to a number—should they be coerced to numbers or bigints before doing the comparison? It makes sense to me that the compiler requires you to explicitly coerce one of them.

Number(1n) == 1.2 // false
1n == BigInt(1.2) // RangeError: The number 1.2 cannot be converted to a BigInt because it is not an integer

Number(BigInt(Number.MAX_SAFE_INTEGER) + 2n) == Number.MAX_SAFE_INTEGER + 2 // true
BigInt(Number.MAX_SAFE_INTEGER) + 2n == BigInt(Number.MAX_SAFE_INTEGER + 2) // false

The compiler gives the same warning if you tried to compare strings and numbers:

// This condition will always return 'false' since the types '"1"' and '1' have no overlap.
'1' == 1 // true

The fact that bigints can't be used as indices is intentional. Arrays, strings, TypedArrays, etc. are meant to be indexed with numbers, so the compiler should warn you if you are indexing with a bigint.
Again, the same is true when strings are used in place of bigints:

// Element implicitly has an 'any' type because index expression is not of type 'number'.ts(7015)
['a', 'b', 'c']['1'] // 'b'

We discussed adding bigint index signatures and eventually decided against it.

@calebsander
I just ran into the issue of comparing a string and a number: '0' != 0 // false

TS2367: This condition will always return 'true' since the types 'string' and '0' have no overlap.

The double equals operator should be thought of as a function with two any parameters since it doesn't do type equality like the triple equals operator does. Or maybe it's possible to improve the core type definitions by encoding the coercion rules into them?

I think it's good that the compiler gives an error in this case. In my experience, comparing values of different types is usually a mistake. If not, you can explicitly convert the number to a string or the string to a number. But I agree that the error message is misleading—perhaps for == it should just say "Cannot compare values of type 'string' and type '0'".

@calebsander
should be error only when strictly

There are lots of things which are technically valid JavaScript, but TypeScript rightly warns about them because they are usually not what the programmer meant to write. For example, you can multiply two strings in JS and they will be coerced to numbers, but TypeScript forces you to explicitly coerce them. I think this is reasonable behavior, and == and === should both require their operands to have the same type.

TS may rightly want to error on people using == at all. But... if someone uses == instead of ===, then TS should accurately make its assumptions about what == will do. It appears from this thread (and others) that's not the case. Wanting to throw useful errors is fine, but incorrectly representing how JS works is not.

IOW, the vastly more common perspective (much to my chagrin) is that devs default to using ===, and that's ESPECIALLY true for those who use type systems like TS. So, if someone is going out of their way to use TS, and they choose a ==, and that's allowed by their linter/settings, then the assumption should be they intended to do that, and that they intended to take advantage of the coercive capabilities of ==.

The interesting thing about comparing numbers with bigints in JavaScript is that it is not implemented by coercing both operands to numbers, nor is it implemented by coercing both arguments to bigints. Instead, the exact real values are compared. Here are some examples:

These values are different:

> 10n**50n == 10**50
false

But they become equal when coerced to numbers:

> Number(10n**50n) == Number(10**50)
true

These values can be compared:

> 1n == 1.5
false

But cannot be coerced to bigints:

> BigInt(1n) == BigInt(1.5)
Uncaught:
RangeError: The number 1.5 cannot be converted to a BigInt because it is not an integer

Currently, it is not possible to get this comparison semantics in TypeScript without using type assertions. I agree that == operator shouldn't have the same strict type checking rules as === operator, as the use of the former signals the intent that the operands may be of different types.

Was this page helpful?
0 / 5 - 0 ratings