aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--_tils/2021-08-11-encoding-and-decoding-javascript-bigint-values-with-reviver.md107
1 files changed, 107 insertions, 0 deletions
diff --git a/_tils/2021-08-11-encoding-and-decoding-javascript-bigint-values-with-reviver.md b/_tils/2021-08-11-encoding-and-decoding-javascript-bigint-values-with-reviver.md
new file mode 100644
index 0000000..f309641
--- /dev/null
+++ b/_tils/2021-08-11-encoding-and-decoding-javascript-bigint-values-with-reviver.md
@@ -0,0 +1,107 @@
+---
+
+title: Encoding and decoding JavaScript BigInt values with reviver
+
+date: 2021-08-11
+
+layout: post
+
+lang: en
+
+ref: encoding-and-decoding-javascript-bigint-values-with-reviver
+
+---
+
+`JSON.parse()` accepts a second parameter: a [`reviver()` function][reviver].
+It is a function that can be used to transform the `JSON` values as they're
+being parsed.
+
+[reviver]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#using_the_reviver_parameter
+
+As it turns out, when combined with JavaScript's [`BigInt`] type, you can parse
+and encode JavaScript `BigInt` numbers via JSON:
+
+[`BigInt`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt
+
+```javascript
+const bigIntReviver = (_, value) => {
+ if (
+ typeof value === "string"
+ && value[value.length - 1] === "n"
+ && value.length > 1
+ ) {
+ const numberStr = value.slice(0, value.length - 1);
+ if (numberStr.match(/^[0-9]+$/) !== null) {
+ return BigInt(numberStr);
+ }
+ }
+ return value;
+};
+```
+
+I chose to interpret strings that contains only numbers and an ending `n` suffix
+as `BigInt` values, similar to how JavaScript interprets `123` (a number)
+differently from `123n` (a `bigint`);
+
+We do those checks before constructing the `BigInt` to avoid throwing needless
+exceptions on the parsing function, as this could easily become a bottleneck
+when parsing large JSON values.
+
+In order to do the full roundtrip, we now only need the `toJSON()` counterpart:
+
+```javascript
+BigInt.prototype.toJSON = function() {
+ return this.toString() + "n";
+};
+```
+
+With both `bigIntReviver` and `toJSON` defined, we can now successfully parse
+and encode JavaScript objects with `BigInt` values transparently:
+
+```javascript
+const s = `[
+ null,
+ true,
+ false,
+ -1,
+ 3.14,
+ "a string",
+ { "a-number": "123" },
+ { "a-bigint": "123n" }
+]`;
+
+const parsed = JSON.parse(s, bigIntReviver);
+const s2 = JSON.stringify(parsed);
+
+console.log(parsed);
+console.log(s2);
+
+console.log(typeof parsed[6]["a-number"])
+console.log(typeof parsed[7]["a-bigint"])
+```
+
+The output of the above is:
+
+```
+[
+ null,
+ true,
+ false,
+ -1,
+ 3.14,
+ 'a string',
+ { 'a-number': '123' },
+ { 'a-bigint': 123n }
+]
+[null,true,false,-1,3.14,"a string",{"a-number":"123"},{"a-bigint":"123n"}]
+string
+bigint
+```
+
+If you're on a web browser, you can probably try copying and pasting the above
+code on the console right now, as is.
+
+Even though [`JSON`] doesn't include `BigInt` number, encoding and decoding them
+as strings is quite trivial on JavaScript.
+
+[`JSON`]: https://datatracker.ietf.org/doc/html/rfc8259