Mongoose调用save报错:IProtein类型无save属性的正确解决方法
解决Mongoose中
Property 'save' does not exist on type 'IProtein'错误(无需继承mongoose.Document) 我在使用Mongoose的save()方法时遇到了类型错误:Property 'save' does not exist on type 'IProtein'.ts(2339)。我知道可以让接口继承mongoose.Document来解决,但官方不推荐这种方式,请问正确的解决方法是什么?
相关代码
模型定义:
import * as mongoose from "mongoose"; interface IProtein { seller: mongoose.Schema.Types.ObjectId; name: string; description: string; price: Number; flavour: string; } const proteinSchema = new mongoose.Schema<IProtein>({ seller: { type: mongoose.Schema.Types.ObjectId, required: true, ref: 'User' }, name: { type: String, required: true, unique: true }, description: { type: String, required: true }, price: { type: Number, required: true }, flavour: { type: String, required: true } }) const Protein: mongoose.Model<IProtein> = mongoose.model('Protein', proteinSchema); export { Protein, IProtein };
更新函数:
export const updateProtein = asyncHandler(async (req: any, res: any) => { const currentName: string = req.params.name const { name, description, price, flavour } : { name: string | null, description: string | null, price: number | null, flavour: string | null } = req.body if (!name && !description && !price && !flavour) { return res.status(400).json({ message: 'No field got changed' }) } const protein: Protein.IProtein | null = await Protein.Protein.findOne({ currentName }).lean().exec() if (!protein) { return res.status(400).json({ message: 'Protein not found' }) } const duplicate: Protein.IProtein[] | null = await Protein.Protein.find({ currentName }).lean().exec() if (duplicate.length > 1) { return res.status(409).json({ message: 'Duplicate protein title' }) } if (protein) { if (name) protein.name = name if (description) protein.description = description if (price) protein.price = price if (flavour) protein.flavour = flavour const updatedProtein = await protein.save() //This is the problem res.json(`'${updatedProtein.name}' updated`) } })
问题根源
.lean()方法的影响:你调用了.lean()查询数据,该方法返回的是普通JavaScript对象,并非Mongoose的Document实例,自然没有save()方法。- 类型定义缺失关联:你的
IProtein接口仅定义了数据结构,未关联Mongoose Document的方法类型,导致TypeScript无法识别save()。
正确解决方法
方案1:移除.lean()并使用HydratedDocument关联类型
Mongoose 6+推荐用HydratedDocument来关联数据接口和Document实例类型,无需继承mongoose.Document:
1.1 修改模型定义
import * as mongoose from "mongoose"; interface IProtein { seller: mongoose.Schema.Types.ObjectId; name: string; description: string; price: number; // 注意改用小写number(原始类型),避免大写Number包装对象 flavour: string; } // 定义带Mongoose方法的文档实例类型 type ProteinDocument = mongoose.HydratedDocument<IProtein>; const proteinSchema = new mongoose.Schema<IProtein>({ seller: { type: mongoose.Schema.Types.ObjectId, required: true, ref: 'User' }, name: { type: String, required: true, unique: true }, description: { type: String, required: true }, price: { type: Number, required: true }, flavour: { type: String, required: true } }) const Protein = mongoose.model<IProtein>('Protein', proteinSchema); export { Protein, IProtein, ProteinDocument };
1.2 修改更新函数
import { Protein, ProteinDocument } from './path-to-your-model'; export const updateProtein = asyncHandler(async (req: any, res: any) => { const currentName: string = req.params.name const { name, description, price, flavour } = req.body as { name?: string, description?: string, price?: number, flavour?: string }; if (!name && !description && !price && !flavour) { return res.status(400).json({ message: '没有修改任何字段' }) } // 移除.lean(),使用ProteinDocument类型 const protein: ProteinDocument | null = await Protein.findOne({ name: currentName }).exec(); if (!protein) { return res.status(400).json({ message: '未找到该产品' }) } // 检查新名称是否重复(修复原代码逻辑错误) if (name) { const duplicate = await Protein.findOne({ name }).lean().exec(); if (duplicate && duplicate._id.toString() !== protein._id.toString()) { return res.status(409).json({ message: '产品名称已存在' }) } } // 更新字段 if (name) protein.name = name; if (description) protein.description = description; if (price) protein.price = price; if (flavour) protein.flavour = flavour; const updatedProtein = await protein.save(); res.json(`'${updatedProtein.name}' 已更新`) })
方案2:使用findOneAndUpdate替代save(更高效)
如果无需修改文档实例再保存,直接用findOneAndUpdate可以跳过Document实例化步骤,更高效:
export const updateProtein = asyncHandler(async (req: any, res: any) => { const currentName: string = req.params.name const updates = {} as Partial<IProtein>; const { name, description, price, flavour } = req.body; // 构建更新对象 if (name) updates.name = name; if (description) updates.description = description; if (price) updates.price = price; if (flavour) updates.flavour = flavour; if (Object.keys(updates).length === 0) { return res.status(400).json({ message: '没有修改任何字段' }) } // 检查名称重复 if (name) { const duplicate = await Protein.findOne({ name }).lean().exec(); if (duplicate) { return res.status(409).json({ message: '产品名称已存在' }) } } // 直接更新并返回新文档 const updatedProtein = await Protein.findOneAndUpdate( { name: currentName }, updates, { new: true, runValidators: true } // new返回更新后的数据,runValidators启用字段验证 ).exec(); if (!updatedProtein) { return res.status(400).json({ message: '未找到该产品' }) } res.json(`'${updatedProtein.name}' 已更新`) })
内容的提问来源于stack exchange,提问作者HenSinai
相关产品推荐
相关产品推荐

