BigInt
Regular JavaScript numbers run out of precision at 2^53 — past Number.MAX_SAFE_INTEGER (9,007,199,254,740,991) you can no longer trust integer arithmetic. BigInt is the language's answer: an arbitrary-precision integer type with its own literal syntax (123n) and its own type tag (typeof 123n === "bigint"). It was added in ES2020 and is now supported in every modern runtime.
Why it exists
// Regular numbers silently break past 2^53 Number.MAX_SAFE_INTEGER; // 9007199254740991 Number.MAX_SAFE_INTEGER + 1; // 9007199254740992 Number.MAX_SAFE_INTEGER + 2; // 9007199254740992 (!) 9007199254740993 === 9007199254740992; // true — collision // BigInt counts every integer exactly 9007199254740993n + 1n; // 9007199254740994n 10n ** 40n; // 10000000000000000000000000000000000000000n
The trailing n marks a BigInt literal. You can also call BigInt(value).
Creating BigInts
const a = 42n; // literal
const b = BigInt(42); // from a number — must be a safe integer
const c = BigInt("12345678901234567890"); // from a string — any size
const d = BigInt(0xff); // from a hex number
const e = BigInt("0b1010"); // from a binary string
BigInt(1.5); // RangeError — not an integer
BigInt("abc"); // SyntaxErrortypeof and identity
typeof 42n; // "bigint" typeof 42; // "number" 42n === 42; // false — different types 42n == 42; // true — == coerces BigInt(42) === 42n; // true
Arithmetic — same operators, separate type
5n + 3n; // 8n 5n - 3n; // 2n 5n * 3n; // 15n 5n / 3n; // 1n — integer division, truncates toward zero 5n % 3n; // 2n 2n ** 64n; // 18446744073709551616n -7n; // -7n // Comparisons work as expected, including against Number 5n < 6; // true 5n === 5; // false 0n == false; // true — both coerce to "zero"
You can't mix BigInt and Number
1n + 1; // TypeError: Cannot mix BigInt and other types 2n * 3; // TypeError Math.sqrt(9n); // TypeError — Math methods don't accept BigInt // You must convert explicitly 1n + BigInt(1); // 2n Number(1n) + 1; // 2 // Watch the conversion direction — Number can lose precision! Number(9007199254740993n); // 9007199254740992 (lossy) BigInt(0.5); // RangeError
Serialisation
JSON.stringify(42n);
// TypeError: Do not know how to serialize a BigInt
JSON.stringify({ id: 42n }, (key, value) =>
typeof value === "bigint" ? value.toString() : value
);
// '{"id":"42"}' — encoded as a stringJSON has no BigInt type. The standard pattern is to send BigInts as strings and parse them back on the other side.
When to actually reach for BigInt
Large database IDs. Postgres
bigint, Twitter snowflake IDs, Discord IDs and YouTube comment IDs all exceed2^53. Parsing them asNumberloses the last few digits.Cryptography and hashing. Modular exponentiation, RSA, elliptic-curve math — anything that touches values bigger than 53 bits.
Financial systems where amounts are stored as integers (e.g. satoshis, micro-cents). You're already in integer-land; BigInt removes the safe-integer ceiling.
Counting things in scientific or combinatorial code — factorials, large permutations, exact statistics over huge datasets.
A real example: snowflake IDs
// A Discord snowflake from the API — looks like a number, but... const raw = "175928847299117063"; // Parsing as Number silently corrupts the last few digits Number(raw); // 175928847299117060 ← wrong! // BigInt preserves it const id = BigInt(raw); // 175928847299117063n // You can do arithmetic on it too — e.g. extract the timestamp const DISCORD_EPOCH = 1420070400000n; const timestamp = Number((id >> 22n) + DISCORD_EPOCH); new Date(timestamp); // a real Date
When NOT to use BigInt
Counts and indexes under a billion. Regular
Numberis faster and interoperates withMath,Array.length, the DOM, etc.Anything that needs fractions. BigInt is integers-only.
Hot loops that interoperate with Number-based code. Conversion costs and the inability to use
Mathmethods can dominate.Floating-point science — BigInt is the wrong tool, use
Numberor a typed array.
Useful patterns
// Safely turn a Number-or-string ID into a BigInt
const toId = v => typeof v === "bigint" ? v : BigInt(v);
// Sum a list of BigInts
const sum = xs => xs.reduce((a, b) => a + b, 0n);
// Min and max — Math.min doesn't accept BigInt, write your own
const minBig = (a, b) => a < b ? a : b;
const maxBig = (a, b) => a > b ? a : b;
// Modular exponentiation, useful in crypto
function modPow(base, exp, mod) {
let result = 1n;
base = base % mod;
while (exp > 0n) {
if (exp & 1n) result = (result * base) % mod;
exp >>= 1n;
base = (base * base) % mod;
}
return result;
}
modPow(2n, 100n, 1_000_000_007n); // 976371285nBigInt is one of those features you can safely ignore until the day you absolutely need it. The moment you see an ID arrive as a 19-digit string, or your sums of "small" integers start producing duplicates, you'll be glad it's there.