BigInt Basics
BigInt for integers beyond Number.MAX_SAFE_INTEGER — literals, ops, JSON gaps, and when Number is still the right tool.
- javascript
- bigint
- numbers
Number is IEEE-754 double. Integers are only safe up to 2^53 - 1 (Number.MAX_SAFE_INTEGER). Past that, IDs and counters silently round. BigInt is arbitrary-precision integers — for snowflake IDs, cryptography-ish counters, and anything that must stay exact.
Creating BigInts
const a = 9007199254740993n; // literal — suffix n
const b = BigInt('9007199254740993');
const c = BigInt(42);
Number.isSafeInteger(9007199254740993); // false — already rounded as Number
9007199254740993n === BigInt('9007199254740993'); // true
Prefer BigInt('...') for values that came from JSON/strings. BigInt(1.5) throws; only integers convert.
Arithmetic
10n + 20n; // 30n
10n * 3n; // 30n
10n / 3n; // 3n — truncates toward zero
10n % 3n; // 1n
2n ** 10n; // 1024n
// mixed types throw
// 1n + 1 → TypeError
1n + BigInt(1); // 2n
Number(1n) + 1; // 2 — but may lose precision for large values
Comparisons between Number and BigInt work; arithmetic does not:
1n < 2; // true
1n == 1; // true
1n === 1; // false
When frontends need it
// Twitter/Snowflake-style IDs as strings in JSON, as BigInt in logic
function idGreater(a, b) {
return BigInt(a) > BigInt(b);
}
idGreater('9007199254740993', '9007199254740992'); // true
// Number compare would already be wrong for these
Bitwise ops work on BigInt (& | ^ ~ << >>), which is handy for flag packs that exceed 32 bits (Number bitwise ops coerce to int32).
JSON and the ecosystem
JSON.stringify({ id: 1n }); // TypeError: Do not know how to serialize a BigInt
JSON.stringify({ id: 1n }, (_, v) =>
typeof v === 'bigint' ? v.toString() : v,
);
// {"id":"1"}
JSON.parse('{"id":"1"}', (key, value) =>
key === 'id' ? BigInt(value) : value,
);
APIs almost always send large IDs as strings. Keep them strings at the boundary; convert to BigInt only when you need math or ordered compare.
TypedArray and many Web APIs still speak Number. Check support before you invent BigInt pipelines through canvas or WebGL.
Division and signs
(-10n) / 3n; // -3n
(-10n) % 3n; // -1n
No >>> for BigInt. Unary + is not allowed on BigInt (+1n is a syntax error in some contexts — use BigInt or just the literal).
Footguns
| Trap | Fix |
|---|---|
| Mixing Number math | Explicit convert both sides |
| JSON.stringify | Replacer → string |
Math.* helpers |
Don’t work on BigInt; use your own |
| Performance | BigInt is slower than Number; don’t use for animation frames |
parseInt on big strings |
Loses precision; use BigInt(str) |
Interview answer
“BigInt holds integers of arbitrary size with an n suffix. You can’t mix them with Number in arithmetic without converting. JSON doesn’t serialize them, so wire formats use strings. I use BigInt for large IDs and exact integer math; Number remains correct for UI measurements and anything under MAX_SAFE_INTEGER.”
Related
- Number precision and IEEE-754
- JSON parse and stringify pitfalls
- Bitwise operators for flags
- Interview prep
Mixed comparisons and sorting
const ids = [10n, 2n, 100n];
ids.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
// cannot use a - b — subtraction works, but subtracting Number breaks; keep BigInt-only
// coerce for display only
function formatId(id) {
return typeof id === 'bigint' ? id.toString() : String(id);
}
Typed arrays and DataView still speak Number for most widths; BigInt64Array / BigUint64Array exist when you need 64-bit integer buffers. Don’t cast large snowflake IDs through Number “just for sorting” — you reintroduce the precision bug you adopted BigInt to fix.
Further reading
Related guides
- AbortControllerCancel fetch and other async work with AbortController and AbortSignal — timeouts, race conditions, and cleanup when components unmount.
- Array find, some, every, includesShort-circuiting array predicates: find, findIndex, some, every, and includes — when to use each and common interview traps.
- Array map, filter, reducemap, filter, and reduce as the core transform toolkit — immutability, chaining costs, reduce patterns, and when a plain loop is clearer.
- Arrow Functions Deep DiveLexical this, no arguments object, no construct, concise bodies — when arrows help and when methods and generators need classic functions.
- Async Iteration and for await...ofAsync iterables, for await...of, and streaming data — how async iterators differ from Promise.all and when to use each.