Большие числа
Доступно автоматическое преобразование больших чисел в специальный тип QBigInt
и обратно при запросах к серверу
с помощью HttpClient
в формате JSON.
Если в ответе на запрос к серверу встречается число больше Number.MAX_SAFE_INTEGER (opens in a new tab), оно будет преобразовано в тип QBigInt
.
Если в запросе к серверу встречается тип QBigInt
, оно будет преобразовано в число без кавычек (см. примеры далее).
Преобразование доступно, начиная с выпуска 7.3.12 и по умолчанию выключено.
Как включить автоматическое преобразование больших чисел
Оно может быть включено для всего стенда настройкой в конфигурации:
{
"common": {
"bigNumbers": {
"autoConvert": true
}
}
}
Также его можно включить/выключить для конкретного продукта при подключении QCoreModule
:
import {DoBootstrap, NgModule} from '@angular/core';
import {QCoreModule,QCoreService} from '@diasoft/qpalette-core';
@NgModule({
imports: [
QCoreModule.forRoot({
...QCoreService.config.common,
bigNumbers: { autoConvert: false }
})
]
})
export class AppModule implements DoBootstrap { /*...*/ }
Как преобразовать данные вручную
Для ручного преобразования JSON с большими числами можно использовать QJsonConverter
:
import {inject} from "@angular/core";
import {QJsonConverter} from '@diasoft/qpalette-core';
const value = '{"bigInt":9223372036854775807}';
const jsonConverter = inject(QJsonConverter);
const withoutLosses = jsonConverter.parse(value);
console.log('Число без потерь:', withoutLosses['bigInt']); // Число без потерь: QBigInt{#rawValue: '9223372036854775807'}
console.log(withoutLosses['bigInt'].toString()); // 9223372036854775807
console.log(JSON.stringify(withoutLosses)); // {"bigInt":9223372036854775807}
При работе с HttpClient
также можно выполнить преобразование вручную:
import {inject} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {QJsonConverter} from '@diasoft/qpalette-core';
const httpClient = inject(HttpClient);
const jsonConverter = inject(QJsonConverter);
httpClient.get('/some/bigint', {responseType: 'text'}) // Отключаем преобразование
.subscribe((value => {
value = jsonConverter.parse(value); // Преобразуем JSON самостоятельно
}));
При этом неважно, включено ли автоматическое преобразование или нет.
Как это работает
Тип QBigInt
– это специальный "промежуточный" тип, который хранит в себе строковое представление числа, полученного
с сервера в формате JSON, если оно превышает Number.MAX_SAFE_INTEGER (opens in a new tab).
Например, если сервера был получен JSON такого вида:
{ "bigInt": 9223372036854775807 }
То при попытке получить его с сервера стандартными средствами оно будет преобразовано в тип number
с потерями:
import {HttpClient} from "@angular/common/http";
import {inject} from "@angular/core";
const httpClient = inject(HttpClient);
httpClient.get('/some/bigint') // HttpClient по умолчанию сам пытается преобразовать JSON
.subscribe((value => {
console.log('Число с потерями:', value['bigInt']); // Число с потерями: 9223372036854776000
console.log(typeof value['bigInt']); // number
}));
httpClient.get('/some/bigint', {responseType: 'text'}) // Можно отключить преобразование
.subscribe((value => {
value = JSON.parse(value); // ...и преобразовать JSON самостоятельно, но результат будет тот же
console.log('Число с потерями:', value['bigInt']); // Число с потерями: 9223372036854776000
console.log(typeof value['bigInt']); // number
}));
Для решения этой проблемы и был придуман тип QBigInt
. При включении преобразования подобные числа будут преобразованы
в QBigInt
:
import {HttpClient} from "@angular/common/http";
import {inject} from "@angular/core";
import {QBigInt} from '@diasoft/qpalette-core';
const httpClient = inject(HttpClient);
httpClient.get('/some/bigint') // HttpClient теперь пытается преобразовать JSON без потерь
.subscribe((value => {
console.log('Число без потерь:', value['bigInt']); // Число без потерь: QBigInt{#rawValue: '9223372036854775807'}
console.log(typeof value['bigInt']); // object
console.log(value['bigInt'] instanceof QBigInt); // true
console.log(value['bigInt'].toString()); // 9223372036854775807
console.log(JSON.stringify(value)); // {"bigInt":9223372036854775807}
}));
Обратите внимание, что в случае запроса с {responseType: 'text'}
автоматическое преобразование не будет выполнено:
import {HttpClient} from "@angular/common/http";
import {inject} from "@angular/core";
import {QBigInt,QJsonConverter} from '@diasoft/qpalette-core';
const httpClient = inject(HttpClient);
const jsonConverter = inject(QJsonConverter);
httpClient.get('/some/bigint', {responseType: 'text'}) // В таком случае преобразование не будет выполнено
.subscribe((value => {
const jsonWithLosses = JSON.parse(value); // Так тоже не сработает
console.log('Число с потерями:', jsonWithLosses['bigInt']); // Число с потерями: 9223372036854776000
console.log(typeof jsonWithLosses['bigInt']); // number
console.log(jsonWithLosses['bigInt'] instanceof QBigInt); // false
const jsonWithoutLosses = jsonConverter.parse(value); // Но можно преобразовать JSON с помощью конвертера без потерь
console.log('Число без потерь:', jsonWithoutLosses['bigInt']); // Число без потерь: QBigInt{#rawValue: '9223372036854775807'}
}));
Почему не встроенный тип BigInt?
BigInt
не имеет встроенной реализации метода toJSON
, поэтому для автоматической конвертации JSON
в BigInt
и обратно
требуется переопределять прототип (см. раздел на MDN (opens in a new tab)).
Поскольку Q.Palette работает в сложной среде микрофронтендов, в каждом из которых может быть своя реализация метода
toJSON
, было решено ввести свой тип с заранее заданным прототипом.
Помимо этого, BigInt
не поддерживает числа с плавающей точкой, QBigInt
решает эту проблему.
Как работать с QBigInt
Класс имеет свои реализации методов toJSON
и toString
, поэтому его объекты можно приводить к строке и к формату JSON:
import {QBigInt} from '@diasoft/qpalette-core';
const bigInt = new QBigInt("9223372036854775807");
console.log('Число без потерь:', bigInt); // Число без потерь: QBigInt{#rawValue: '9223372036854775807'}
console.log(bigInt.toString()); // 9223372036854775807
console.log(JSON.stringify({"bigInt": bigInt})); // {"bigInt":9223372036854775807}
Обратите внимание, что сменить реализацию toJSON
и toString
класса QBigInt
не получится, так как его прототип
защищён от изменений:
import {QBigInt} from '@diasoft/qpalette-core';
const bigInt = new QBigInt("9223372036854775807");
bigInt.toJSON = function () { /*...*/ } // ошибка
bigInt.toString = function () { /*...*/ } // ошибка
Операции с большими числами
QBigInt
не поддерживает операции над числами. Для выполнения операций с числом преобразуйте объект QBigInt
самостоятельно во встроенный BigInt
или используйте библиотеку bignumber.js (opens in a new tab)
(не входит в состав Q.Palette).
При преобразовании в BigInt
убедитесь, что число не является числом с плавающей точкой, иначе будет ошибка
(см. следующий раздел).
const bigInt = new QBigInt("9223372036854775807");
const a = BigInt(bigInt.toString());
const b = BigInt(1);
const sum = a + b;
console.log(sum); // 9223372036854775808n
const bigFloat = new QBigInt("1.234567890123456789");
const floatToInt = BigInt(bigInt.toString()); // ошибка
Числа с плавающей точкой
Встроенный тип BigInt
не поддерживает числа с плавающей точкой. Однако QBigInt
не имеет такой проблемы.
При включенной конвертации большие числа с плавающей точкой также будут преобразованы в QBigInt
и будут храниться
в строковом виде.
Поддерживаются как числа в стандартной форме, так и экспоненциальной.
import {QBigInt} from '@diasoft/qpalette-core';
// обычная форма
const bigFloat = new QBigInt("1.234567890123456789");
console.log(bigFloat); // QBigInt{#rawValue: '1.234567890123456789'}
console.log(bigFloat.toString()); // 1.234567890123456789
console.log(JSON.stringify({"bigFloat": bigFloat})); // {"bigInt":1.234567890123456789}
// экспоненциальная форма
const bigFloatExp = new QBigInt("1.234567890123456789e+20");
console.log(bigFloatExp); // QBigInt{#rawValue: '1.234567890123456789e+20'}
console.log(bigFloatExp.toString()); // 1.234567890123456789e+20
console.log(JSON.stringify({"bigFloat": bigFloatExp})); // {"bigInt":1.234567890123456789e+20}
QBigInt
не поддерживает операции над числами с плавающей точкой, для операций с такими числами можно использовать
библиотеку bignumber.js (opens in a new tab) (не входит в состав Q.Palette).
Поддержка компонентами Q.Palette и PrimeNG
Обратите внимание, что компоненты PrimeNG не поддерживают (и не планируют поддерживать (opens in a new tab)) большие числа на момент написания этой статьи. В компонентах Q.Palette также пока не планируется поддержка больших чисел.
При работе с палитрой убедитесь, что большие числа корректно преобразуются из QBigInt
в строки (при передаче в компонент)
и обратно (при передаче на сервер).
Пример
Предположим, с сервера пришел JSON:
[
1,
8172638172638716283761,
1.234567890123456789,
1.234567890123456789e+20,
1.23e+20,
{
"normalInt": 123,
"bigInt": 9223372036854775807,
"justString": "9223372036854775807",
"nullValue": null,
"innerObj": {
"normalInt": 234,
"bigInt" : 18276387162397129873,
"someString": "test"
}
}
]
При включенной конвертации следующие числа будут преобразованы в QBigInt
:
[
8172638172638716283761,
1.234567890123456789,
1.234567890123456789e+20,
9223372036854775807,
18276387162397129873
]
Следующие числа будут распознаны как обычный number
:
[
1,
123000000000000000000,
123,
234
]