Support for arbitrarily large integers (BigInts) is a stage 3 TC39 proposal. Stage 3 means the proposal is ready and for browsers to start implementing. Earlier this year, the V8 team added support for BigInts, which means Node.js 10.4.0 has BigInt support. You can use BigInts in Node.js 10.x without any flags, as long as you're on at least 10.4.
$ ~/Workspace/libs/node-v10.4.0-linux-x64/bin/node --version
v10.4.0
$ ~/Workspace/libs/node-v10.4.0-linux-x64/bin/node
> 10000000000n * 10000000000n
100000000000000000000n
>
What Are BigInts?
BigInt is a new JavaScript primitive type that can store arbitrarily large integers, like the Busy Beaver sequence or values of the Ackermann sequence. You can store arbitrarily large numbers as strings, but BigInts have the benefit of supporting arithmetic operators. In other words, you can add and multiply BigInts like you would normal numbers.
There are 2 ways to create a BigInt: either by appending 'n' at the end of an integer literal, or by using the BigInt()
constructor:
42n; // This is a BigInt
BigInt('42'); // This is equivalent
BigInt(42); // Also works
BigInts are not numbers, they are a completely new type. They're the first new primitive type added to JavaScript since Symbols in ES2015. In Node.js 10.4, the primitive data types are:
- Boolean
- Null
- Undefined
- Number
- String
- Symbol
- BigInt
To check if a value is a BigInt, you can use the typeof
operator.
typeof 42n; // "bigint". Note that `typeof` returns all-lowercase.
BigInt equality is strictly by value.
const x = 42n;
let y = 42n;
x === y; // "true"
++y;
x === y; // "false"
Arithmetic On BigInts
The +
, *
, -
, **
, and %
operators work as you would expect between 2
BigInts. You can also use ++
and --
as you would with numbers.
5n + 3n; // "8n"
5n * 3n; // "15n"
5n - 3n; // "2n"
5n ** 3n; // "125n"
5n % 3n; // "2n"
let x = 1;
++x; // "2"
x++; // "3"
x--; // "2"
Division, the /
operator, is slightly trickier. BigInts are always integers,
and the TC39 spec says that BigInt division always rounds down. So even though 5 / 3
is approximately 1.6666
, 5n / 3n === 1n
;
Arithmetic operators between BigInts and numbers do not work. Multiplying a BigInt by a number throws an error.
// The below all throw "TypeError: Cannot mix BigInt and other types, use explicit conversions"
5n + 3;
5n * 3;
5 - 3n;
5 ** 3n;
5 % 3n;
Comparisons, like >
, >=
, <
, and <=
, work as you would expect, even when comparing BigInts to numbers.
42n > 42; // false
42n >= 42; // true
43n > 42; // true
42n < 43; // true
42n <= 42; // true
42n == 42; // true
// A BigInt is never strictly equal (===) to a number
42n === 42; // false
Currently, JavaScripts Math
utility functions do not support BigInts. While you can use the exponentiation operator **
, using Math.pow()
throws an error:
Math.pow(5n, 3n); // TypeError: Cannot convert a BigInt value to a number
Limitations
There are several limitations to using BigInts. Because BigInts are a new
primitive type, many existing libraries and frameworks do not support them.
Currently, you can't even JSON.stringify()
an object that contains a BigInt.
// Throws "TypeError: Do not know how to serialize a BigInt"
JSON.stringify({ answer: 42n });
To work around this limitation, you can add a toJSON()
function to BigInt's prototype:
BigInt.prototype.toJSON = function() { return this.toString(); };
JSON.stringify({ answer: 42n }); // '{"answer":"42"}'
Similarly, the MongoDB driver and Mongoose currently do not support BigInts. The MongoDB driver will silently ignore properties whose values are BigInts.
const { MongoClient } = require('mongodb');
run().catch(error => console.error(error.stack));
async function run() {
const client = await MongoClient.connect('mongodb://localhost:27017/test', { useNewUrlParser: true });
const db = client.db();
await db.dropDatabase();
await db.collection('BigInt').insertOne({ answer: 42n });
const res = await db.collection('BigInt').findOne();
console.log(res); // "{ _id: 5b97e5458015164793387b9e }"
}
Currently the only workaround for this is to add a toBSON()
function to BigInt.prototype
. This will tell the MongoDB driver to convert the BigInt to a string before storing it in MongoDB.
const { MongoClient } = require('mongodb');
run().catch(error => console.error(error.stack));
async function run() {
const client = await MongoClient.connect('mongodb://localhost:27017/test', {
useNewUrlParser: true
});
const db = client.db();
await db.dropDatabase();
// Add a `toBSON()` function to enable MongoDB to store BigInts as strings
BigInt.prototype.toBSON = function() { return this.toString(); };
await db.collection('BigInt').insertOne({ answer: 42n });
const res = await db.collection('BigInt').findOne();
console.log(res); // "{ _id: 5b97e5458015164793387b9e, answer: '42' }"
}
React doesn't currently support passing in BigInts as a child to React.createElement()
either:
const { renderToString } = require('react-dom/server');
const { createElement, Component } = require('react');
class MyComponent extends Component {
render() {
// Will silently ignore the '42n'
return createElement('h1', null, 42n);
}
}
// <h1 data-reactroot=""></h1>
console.log(renderToString(createElement(MyComponent)));
Moving On
BigInts are a very exciting new feature in Node.js 10.4. But, before you start using it, be careful: many libraries and frameworks don't support BigInts, not even native JSON.parse()
and JSON.stringify()
. Also, remember that stage 3 proposals have been withdrawn before. There is no guarantee that BigInts as described here will be part of the JavaScript language beyond Node.js 10.x. That being said, I'm looking forward to implementing BigInt support in Mongoose. So download Node.js 10.x and start trying out BigInts!