You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
'bigint' is not comparable to 'number' with loose equality #30540
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// false1n==BigInt(1.2)// RangeError: The number 1.2 cannot be converted to a BigInt because it is not an integerNumber(BigInt(Number.MAX_SAFE_INTEGER)+2n)==Number.MAX_SAFE_INTEGER+2// trueBigInt(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.
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'".
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 ==.
Reacted by ExE Boss, Bruno Fantauzzi, Artur Klesun, Bradley Odell, Ted Newman, Trevor Paley and Eric Lavault
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:
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.
Reacted by ExE Boss, Artur Klesun, Michal Srb, Piotr Fusik and Eric Lavault
This error makes sense for ===, but not for ==. As Evgeny Kapun (@abacabadabacaba) explained, there is no easy workaround of casting operands to number or bigint.
Note that it's not clear that that's such a useful operation, since BigInts' reason for existing is to handle values outside of the safe integer range, and there be dragons there (as noted in #30540 (comment)):
Therefore, it may well in practice be inappropriate to compare using the == behavior; instead, one of the following may be preferable:
functioncompare(n: number,bi: bigint): boolean{returnNumber.isSafeInteger(n)&&BigInt(n)===bi;}functioncompare(n: number,bi: bigint): boolean{if(!Number.isSafeInteger(n)){thrownewError("attempted to compare unsafe number with bigint");}returnBigInt(n)===bi;}
Providing this info not particularly to weigh in on whether the == should be allowed here in TS, but as a reference for some relevant info on the topic that hasn't been brought up in this thread yet, and may be helpful to others like me who stumble upon this issue.
Note that it's not clear that that's such a useful operation,
I don't think I agree. The problem in this example isn't at all related to BigInt or the comparison, it is just that you are leaving the range of a Number. That is a questionable thing to do whether or not BigInt is involved. By that argument you can say that === should be banned on numbers because Number.MAX_SAFE_INTEGER + 1.5 === Number.MAX_SAFE_INTEGER + 1.
By that argument you can say that === should be banned on numbers because Number.MAX_SAFE_INTEGER + 1.5 === Number.MAX_SAFE_INTEGER + 1.
This isn't that preposterous a conclusion 🙂! In most programming languages we do avoid exact equality comparison between floats, and use a separate integer type (or multiple) for integer operations.
It's just in JS, as far as I'm aware, where it's canonical to use the mantissa of floats to represent exact integers, and so we make a habit of performing integer-like operations on floats.
Anyways, the important points I was trying to make were simply:
You can replicate the == behavior without using the == operator or a type assertion
But please use that with caution depending on your specific context. Don't blame me if it was a bad idea for your use case 🫶
Activity
[-]bigin cant == number[/-][+]'bigint' is not comparable to 'number' with loose equality[/+]calebsander commentedon May 23, 2019
I agree that the error message
This condition will always return 'false'is not strictly correct. But comparingnumberandbigintvalues for equality should definitely cause a type-checking error. It is not obvious how to compare abigintto anumber—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.The compiler gives the same warning if you tried to compare strings and numbers:
The fact that
bigints can't be used as indices is intentional.Arrays,strings,TypedArrays, etc. are meant to be indexed withnumbers, so the compiler should warn you if you are indexing with abigint.Again, the same is true when
strings are used in place ofbigints:We discussed adding
bigintindex signatures and eventually decided against it.BTOdell commentedon Jun 8, 2019
Caleb Sander Mateos (@calebsander)
I just ran into the issue of comparing a string and a number:
'0' != 0 // falseTS2367: 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
anyparameters 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?calebsander commentedon Jun 8, 2019
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'".2A5F commentedon Jun 9, 2019
Caleb Sander Mateos (@calebsander)
should be error only when strictly
calebsander commentedon Jun 9, 2019
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.getify commentedon Mar 6, 2020
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==.abacabadabacaba commentedon Dec 1, 2020
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:
But they become equal when coerced to numbers:
These values can be compared:
But cannot be coerced to bigints:
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.pfusik commentedon Feb 6, 2023
This error makes sense for
===, but not for==. As Evgeny Kapun (@abacabadabacaba) explained, there is no easy workaround of casting operands tonumberorbigint.Please fix it.
kirkwaiblinger commentedon Dec 24, 2024
I believe (but could be wrong) that the following replicates the
==behaviorNote that it's not clear that that's such a useful operation, since
BigInts' reason for existing is to handle values outside of the safe integer range, and there be dragons there (as noted in #30540 (comment)):Therefore, it may well in practice be inappropriate to compare using the
==behavior; instead, one of the following may be preferable:Providing this info not particularly to weigh in on whether the
==should be allowed here in TS, but as a reference for some relevant info on the topic that hasn't been brought up in this thread yet, and may be helpful to others like me who stumble upon this issue.kevincox commentedon Dec 24, 2024
I don't think I agree. The problem in this example isn't at all related to BigInt or the comparison, it is just that you are leaving the range of a Number. That is a questionable thing to do whether or not BigInt is involved. By that argument you can say that
===should be banned on numbers becauseNumber.MAX_SAFE_INTEGER + 1.5 === Number.MAX_SAFE_INTEGER + 1.kirkwaiblinger commentedon Dec 24, 2024
This isn't that preposterous a conclusion 🙂! In most programming languages we do avoid exact equality comparison between floats, and use a separate integer type (or multiple) for integer operations.
It's just in JS, as far as I'm aware, where it's canonical to use the mantissa of floats to represent exact integers, and so we make a habit of performing integer-like operations on floats.
Anyways, the important points I was trying to make were simply:
You can replicate the
==behavior without using the==operator or a type assertionBut please use that with caution depending on your specific context. Don't blame me if it was a bad idea for your use case 🫶