Просто пришел ко мне во сне, как это происходит, когда вы слишком много думаете о чем-то, поэтому мне пришлось встать и записать это здесь, прежде чем я забуду ... :)
Мыможет ввести полные 123n
числа, а затем снять их с кавычек с помощью RegEx:
function toJson(data) {
return JSON.stringify(data, (_, v) => typeof v === 'bigint' ? `${v}n` : v)
.replace(/"(-?\d+)n"/g, (_, a) => a);
}
Он делает именно то, что нужно, и это быстро. Единственным недостатком является то, что если в вашем data
значении установлена строка, подобная 123n
, она станет открытым числом, но вы можете легко запутать ее выше, например, ${^123^}
или 123-bigint
, алгоритм позволяет это легко.
Что касается вопроса, операция не должна быть обратимой, поэтому, если вы используете JSON.parse
для результата, это будет number
-s, теряя что-либоэто как раз между 2^53
и 2^64 - 1
, как и ожидалось.
Кто бы сказал, что это невозможно, а? :)
UPDATE-1
Для совместимости с JSON.stringify
, undefined
должно привести к undefined
. И в рамках фактической реализации pg-обещание я сейчас использую шаблон "123#bigint"
, чтобы сделать случайное совпадение менее вероятным.
И вот итоговый код оттуда:
function toJson(data) {
if (data !== undefined) {
return JSON.stringify(data, (_, v) => typeof v === 'bigint' ? `${v}#bigint` : v)
.replace(/"(-?\d+)#bigint"/g, (_, a) => a);
}
}
ОБНОВЛЕНИЕ-2
Проходя через комментарии ниже, вы можете сделать это безопасно, подсчитав количество замен в соответствии с инъекциями BigInt
и бросивошибка при несоответствии:
function toJson(data) {
if (data !== undefined) {
let intCount = 0, repCount = 0;
const json = JSON.stringify(data, (_, v) => {
if (typeof v === 'bigint') {
intCount++;
return `${v}#bigint`;
}
return v;
});
const res = json.replace(/"(-?\d+)#bigint"/g, (_, a) => {
repCount++;
return a;
});
if (repCount > intCount) {
// You have a string somewhere that looks like "123#bigint";
throw new Error(`BigInt serialization pattern conflict with a string value.`);
}
return res;
}
}