Skip to content
JavaScript js numbers 4 min read

BigInt

JavaScript’s regular Number type can only safely represent integers up to 2^53 - 1. Beyond that boundary, arithmetic silently loses precision and two different values can become indistinguishable. BigInt, introduced in ES2020, is a primitive type for arbitrarily large integers — limited only by available memory — making it the right tool for large database IDs, high-precision counters, and exact integer math.

Why the safe integer limit matters

A standard Number is a 64-bit IEEE 754 double, so it can only hold integers exactly up to Number.MAX_SAFE_INTEGER. Past that point, adding one may produce the same result twice.

console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991

const big = 9007199254740991;
console.log(big + 1); // 9007199254740992
console.log(big + 2); // 9007199254740992  — wrong!
console.log(big + 1 === big + 2); // true   — both collapsed

Output:

9007199254740991
9007199254740992
9007199254740992
true

BigInt avoids this entirely because it stores integers exactly, with no floating-point rounding.

Creating BigInts

There are two ways to make a BigInt: append an n to an integer literal, or call the BigInt() function.

const a = 9007199254740993n;       // literal with the n suffix
const b = BigInt(9007199254740993); // from a Number (beware precision!)
const c = BigInt("9007199254740993"); // from a string — safest for big values

console.log(a);        // 9007199254740993n
console.log(typeof a); // "bigint"
console.log(c === a);  // true

Output:

9007199254740993n
bigint
true

Prefer BigInt("...") over BigInt(someNumber) for values above the safe range. The number literal is already rounded before it ever reaches BigInt(), so the wrong value gets converted.

Operations

BigInt supports the standard arithmetic operators. Division truncates toward zero — there are no fractional results, because BigInt is integers-only.

console.log(10n + 3n);  // 13n
console.log(10n - 3n);  // 7n
console.log(10n * 3n);  // 30n
console.log(10n / 3n);  // 3n   — truncated, not 3.333...
console.log(10n % 3n);  // 1n
console.log(2n ** 64n); // 18446744073709551616n

Output:

13n
7n
30n
3n
1n
18446744073709551616n

The unary + operator is not supported on BigInt (it would force a Number conversion), but comparison operators work across types using loose comparison.

The no-mixing rule

You cannot mix BigInt and Number in arithmetic. Doing so throws a TypeError, because the engine refuses to guess whether you want exact or floating-point math.

try {
  const result = 10n + 5; // mixing types
} catch (err) {
  console.log(err.constructor.name); // "TypeError"
}

// Convert explicitly to combine them:
console.log(10n + BigInt(5));   // 15n
console.log(Number(10n) + 5);   // 15

Output:

TypeError
15n
15

Comparison is more relaxed. Loose equality (==) and the relational operators compare by mathematical value across types, while strict equality (===) also checks the type.

console.log(10n == 10);  // true  — same value
console.log(10n === 10); // false — different types
console.log(10n < 15);   // true
console.log(20n > 15);   // true

Output:

true
false
true
true

Type coercion reference

ConversionCodeResult
Number → BigIntBigInt(42)42n
String → BigIntBigInt("42")42n
BigInt → NumberNumber(42n)42
BigInt → StringString(42n) or 42n.toString()"42"
Boolean contextBoolean(0n) / Boolean(5n)false / true

Note that JSON.stringify cannot serialize a BigInt and will throw. Convert to a string first if you need to send one over the wire.

const payload = { id: 9007199254740993n };

try {
  JSON.stringify(payload);
} catch (err) {
  console.log(err.constructor.name); // "TypeError"
}

console.log(JSON.stringify({ id: payload.id.toString() }));

Output:

TypeError
{"id":"9007199254740993"}

A practical use case: large IDs

Many systems (Twitter/X snowflake IDs, Discord IDs, 64-bit database keys) exceed the safe integer range. Parsing them as Number corrupts the value; BigInt keeps them exact.

const rawId = "1234567890123456789"; // a 64-bit ID from an API

const asNumber = Number(rawId);
const asBigInt = BigInt(rawId);

console.log(asNumber.toString()); // 1234567890123456800 — corrupted
console.log(asBigInt.toString()); // 1234567890123456789 — exact

Output:

1234567890123456800
1234567890123456789

Limitations

  • BigInt is integer-only: there is no 1.5n, and division truncates.
  • The Math object does not accept BigInt — Math.sqrt(16n) throws.
  • Mixing with Number in arithmetic throws a TypeError.
  • JSON.stringify throws on BigInt values.
  • It is slower than Number for small values, so don’t reach for it by default.

Best Practices

  • Use BigInt only when you genuinely need integers beyond Number.MAX_SAFE_INTEGER — small math is faster and simpler with Number.
  • Construct large values from strings (BigInt("...")) to avoid the float-rounding trap of BigInt(someNumber).
  • Keep a value either BigInt or Number throughout a calculation; convert explicitly at boundaries rather than mixing mid-expression.
  • Serialize BigInts as strings for JSON, and parse them back with BigInt() on the receiving end.
  • Use the n literal suffix for clarity when values are known at author time.
  • Remember division truncates — if you need a remainder, use the % operator alongside it.
Last updated June 1, 2026
Was this helpful?