使用sinclair/typebox时Number()无法正确转换Oracle数据库返回的浮点数问题求助
使用sinclair/typebox时Number()无法正确转换Oracle数据库返回的浮点数问题求助
这个问题本质是JavaScript原生Number的浮点数精度限制导致的——Oracle返回的数值在转成JS Number时出现了精度溢出,和TypeBox本身关系不大,但我们可以结合TypeBox的特性来优雅解决。给你几个实用的方案:
方案1:从数据库查询阶段直接处理精度
最省心的方式是让Oracle返回精确格式化后的数值,避免前端处理麻烦。可以在SQL里用ROUND或者TO_CHAR函数控制小数位数:
-- 直接四舍五入到两位小数 SELECT ROUND(itemBalance, 2) AS itemBalance FROM your_table; -- 或者先转成指定格式的字符串再转回数值,确保精度 SELECT TO_NUMBER(TO_CHAR(itemBalance, 'FM999999999.99')) AS itemBalance FROM your_table;
这样数据库返回的就是精确到两位小数的数值,转成JS Number后不会出现精度偏差。
方案2:在TypeBox中自定义转换逻辑处理精度
TypeBox支持通过Type.Transform添加数据转换规则,我们可以定义一个带精度处理的自定义Number类型,自动修正数值:
import { Type, Static, Transform } from '@sinclair/typebox'; // 自定义一个默认处理两位小数的Number类型 const Decimal2 = Transform(Type.Number()) .Decode((value) => Number(value.toFixed(2))) .Encode((value) => value); // 替换原Schema里的Type.Number() export const ItemValue = Type.Object({ itemBalance: Decimal2 }) export type ItemValueType = Static<typeof ItemValue>;
这样当数据从数据库返回后,TypeBox会自动把数值四舍五入到指定小数位,完美解决精度问题。
方案3:改用Decimal类替代原生Number(适合高精度场景)
如果是金融类对精度要求极高的项目,建议直接用decimal.js这类专门处理高精度数值的库,从根源上规避浮点数问题。同时可以在TypeBox中定义对应的类型:
import { Type, Static } from '@sinclair/typebox'; import Decimal from 'decimal.js'; // 自定义Decimal类型 const DecimalType = Type.Unsafe<Decimal>(); export const ItemValue = Type.Object({ itemBalance: DecimalType }) export type ItemValueType = Static<typeof ItemValue>; // 查询时直接将数据库返回值转为Decimal实例 const result = await connection.execute(query, {}, { outFormat: OUT_FORMAT_OBJECT }); const itemValue: ItemValueType = { itemBalance: new Decimal(result.rows[0].itemBalance) };
这种方式彻底避免了JS原生Number的精度缺陷,适合对数值准确性要求严格的场景。
临时应急方案
如果只是单个场景需要处理,也可以在拿到查询结果后手动修正:
const itemBalance = Number(result.rows[0].itemBalance.toFixed(2));
不过这种方式不够优雅,还是推荐前面的方案1或方案2。
备注:内容来源于stack exchange,提问作者Toseef Zafar
相关产品推荐
相关产品推荐

