You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 09:48:20