Mongoose+TypeScript如何存取MongoDB Decimal128值(保留4位小数精度)
问题根因
你遇到的类型报错核心原因是Mongoose 5.x的Schema泛型校验会严格匹配TS接口中声明的字段类型:你在ITransaction接口中将trxValue声明为string,TS会默认该字段的Schema配置只能对应字符串类型规则,和你实际配置的Schema.Types.Decimal128类型冲突,就会抛出该类型不匹配错误。
完整可运行实现
第一步:修正接口定义
区分数据库存储的原生类型和getter转换后的业务返回类型,使用Mongoose内置的Types.Decimal128对应数据库存储的Decimal值:
// ITransaction.ts interface file import { Types, Document } from 'mongoose'; // 业务层操作的交易实体类型 export interface ITransaction { trxNo: string; trxType: 'Credit' | 'Debit'; trxDate: Date; trxDesc: string; trxValue: Types.Decimal128; cutomerId: Types.ObjectId; accountId: Types.ObjectId; } // 模型实例对应的Document类型,查询返回的实例统一用该类型 export type TransactionDocument = ITransaction & Document;
第二步:修正Schema配置
显式配置字段的写入、读取逻辑,同时开启getter自动生效配置,保证读写时都自动处理4位小数精度:
// Transactions.ts model file import { model, Schema, Types } from 'mongoose'; import { ITransaction, TransactionDocument } from '../interfaces/ITransaction'; const trxSchema = new Schema<TransactionDocument>({ trxNo: { type: String, required: true, unique: true }, trxType: { type: String, required: true, enum: ['Credit', 'Debit'] }, trxDate: { type: Date, default: Date.now }, trxDesc: { type: String, required: true }, trxValue: { type: Schema.Types.Decimal128, required: true, // 读取时自动格式化,固定返回4位小数字符串 get: (v: Types.Decimal128): string => { if (!v) return '0.0000'; return (+v.toString()).toFixed(4); }, // 写入时自动做精度转换,从源头保证存储值精度 set: (v: number | string): Types.Decimal128 => { const formattedVal = Number(v).toFixed(4); return Types.Decimal128.fromString(formattedVal); } }, cutomerId: { type: Schema.Types.ObjectId, required: true, ref: 'Customer' }, accountId: { type: Schema.Types.ObjectId, required: true, ref: 'Account' }, }, { // 关键配置:序列化/转普通对象时自动触发getter,返回格式化后的值 toJSON: { getters: true }, toObject: { getters: true } }); const Transaction = model<TransactionDocument>('Transaction', trxSchema); export default Transaction;
第三步:读写验证示例
import { Types } from 'mongoose'; import Transaction from './Transactions'; // 新增数据示例 async function createTestTransaction() { const newTrx = await Transaction.create({ trxNo: 'TRX2024050001', trxType: 'Credit', trxDesc: '账户余额充值', // 传入数字、字符串格式的数值都可自动转换 trxValue: 256.789456, cutomerId: new Types.ObjectId(), accountId: new Types.ObjectId() }); // 直接拿到格式化后的4位小数字符串,无需额外转换 console.log(newTrx.trxValue); // 输出 "256.7895"(自动四舍五入) console.log(typeof newTrx.trxValue); // 输出 "string" } // 查询数据示例 async function getTransactionById(trxId: string) { const trxRecord = await Transaction.findById(trxId); if (trxRecord) { console.log(trxRecord.trxValue); // 直接返回4位精度字符串,例如 "1200.4500" } }
注意事项
- 不要在接口中直接把
trxValue定义为string,否则会触发泛型校验错误,Mongoose的TS类型会自动根据getter返回值推导业务层拿到的字段类型 - 必须配置
toJSON.getters和toObject.getters为true,否则自定义getter不会在查询结果中生效,拿到的还是原生Decimal128对象 - 写入环节加setter做精度拦截,避免存入超过4位小数的脏数据
- 涉及金额计算场景,不要直接用返回的字符串做运算,可转数字或使用专用高精度计算库处理,避免浮点数精度误差
内容的提问来源于stack exchange,提问作者Salvino D'sa
相关产品推荐
相关产品推荐

