Mongoose pre save钩子报错:类型“save”无法分配给参数类型
Mongoose pre("save")钩子TypeScript类型错误问题排查
问题描述
我正在使用Mongoose为应用创建用户模型,参考官方文档编写了代码,但在设置文档保存的pre钩子时,"save"处出现TypeScript错误。
错误提示
No overload matches this call. The last overload gave the following error. Argument of type '"save"' is not assignable to parameter of type 'RegExp | "insertMany"'.ts(2769)
相关代码
import { NextFunction } from 'express'; import { Schema, model } from 'mongoose'; const bcrypt = require("bcrypt") export interface IUser { email: string password: string username: string } const UserSchema = new Schema<IUser>( { email: { type: String, required: true }, password: { type: String, required: true, minlength: 5, hide: true }, username: { type: String, required: true, minlength: 2 }, }, ) UserSchema.pre("save", async function(next: NextFunction) { const user = this; if(!user.isModified("password")){ next(); } bcrypt.genSalt(10, (err: Error, salt: string) => { if(err) { return next(err); } bcrypt.hash(user.password, salt, (err: Error, hash: string) => { if(err){ return next(err); } user.password = hash; next(); }) }) }); UserSchema.methods.comparePassword = function (password: string, cb: Function) { const user = this; bcrypt.compare(password, user.password, (err: Error, isMatch: boolean) => { if(err) { return cb(err); } cb(null, isMatch); }) } const User = model<IUser>("User", UserSchema); export default User
问题原因及解决方法
核心原因
- async函数与回调混用导致类型冲突:声明了
async的pre钩子,但内部使用bcrypt的回调式API,TypeScript无法正确推断next函数的类型匹配。 - Schema泛型类型不完整:仅传入
IUser作为Schema的泛型参数,缺少Mongoose文档内置方法(如isModified)的类型定义,引发类型推断异常。 - 字段配置错误:
password字段的hide不是Mongoose内置选项,会导致类型识别异常。
分步解决
- 统一使用async/await风格重构pre钩子
替换bcrypt的回调API为Promise版本,同时明确this的类型:
UserSchema.pre("save", async function(next: NextFunction) { const user = this as IUser & { isModified: (path: string) => boolean }; // 密码未修改则直接跳过 if (!user.isModified("password")) { return next(); } try { // 生成盐并哈希密码 const salt = await bcrypt.genSalt(10); user.password = await bcrypt.hash(user.password, salt); next(); } catch (err) { // 捕获错误并传递给next next(err as Error); } });
- 完善Schema的泛型定义
使用Mongoose提供的HydratedDocument包含文档内置方法类型,同时修正字段配置:
import { Schema, model, HydratedDocument } from 'mongoose'; export interface IUser { email: string; password: string; username: string; } // 生成包含Mongoose文档方法的类型 type UserDocument = HydratedDocument<IUser>; const UserSchema = new Schema<UserDocument>({ email: { type: String, required: true }, password: { type: String, required: true, minlength: 5, select: false }, // 替换hide为内置的select: false username: { type: String, required: true, minlength: 2 }, });
- 优化comparePassword方法的类型
替换回调风格为Promise风格,避免模糊的Function类型:
UserSchema.methods.comparePassword = async function(password: string): Promise<boolean> { return bcrypt.compare(password, this.password); };
- 确认Mongoose版本兼容性
确保使用Mongoose v6及以上版本(v6+内置完善的TypeScript支持,无需单独安装@types/mongoose),旧版本建议升级到最新稳定版。
内容的提问来源于stack exchange,提问作者user13041346
相关产品推荐
相关产品推荐

