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

Express TypeScript中Mongoose pre find钩子类型错误排查

解决Mongoose + TypeScript中pre(/^find/)钩子的类型错误

问题背景

使用Express.js + TypeScript + Mongoose开发,User模型代码如下:

import mongoose, { Schema, Document, Model } from 'mongoose';
import crypto from 'crypto';
import validator from 'validator';
import bcrypt from 'bcryptjs';
import roles from '../constant/role';

export interface IUser extends Document {
  firstName: string;
  lastName: string;
  phone: string;
  email: string;
  password: string;
  role: string;
  dateOfBirth?: Date;
  emailVerified?: boolean;
  phoneVerified?: boolean;
  updatedBy?: Schema.Types.ObjectId;
  createdBy?: Schema.Types.ObjectId;
  passwordChangedAt?: Date;
  passwordResetToken?: string;
  passwordResetExpires?: Date;
  emailVerificationToken?: string;
  emailVerificationExpires?: Date;
  active?: boolean;
  fullName?: string;
}

export interface IUserMethods {
  correctPassword(
    candidatePassword: string,
    userPassword: string
  ): Promise<boolean>;
  changedPasswordAfter(jwtTimeStamp: number): boolean;
  createPasswordResetToken(): string;
  createEmailVerificationToken(): string;
}

type UserModel = Model<IUser, {}, IUserMethods>;

const UserSchema = new Schema<IUser, UserModel, IUserMethods>(
  {
    firstName: {
      type: String,
      required: [true, 'First Name is required'],
      trim: true,
    },
    lastName: { type: String, trim: true },
    phone: {
      type: String,
      trim: true,
      unique: true,
      required: [true, 'Phone is required'],
    },
    email: {
      type: String,
      required: [true, 'Email is required'],
      unique: true,
      validate: [validator.isEmail, 'Email is not valid'],
      trim: true,
    },
    password: {
      type: String,
      required: [true, 'Password is required'],
      select: false,
    },
    dateOfBirth: { type: Date },
    emailVerified: { type: Boolean, default: false },
    phoneVerified: { type: Boolean, default: false },
    role: {
      type: String,
      enum: [roles.SUPER_ADMIN, roles.ADMIN, roles.STUDENT, roles.GUEST],
      default: roles.GUEST,
    },
    updatedBy: { type: Schema.Types.ObjectId, ref: 'User' },
    createdBy: { type: Schema.Types.ObjectId, ref: 'User' },
    passwordChangedAt: { type: Date },
    passwordResetToken: String,
    passwordResetExpires: Date,
    emailVerificationToken: String,
    emailVerificationExpires: Date,
    active: {
      type: Boolean,
      default: true,
      select: false,
    },
  },
  {
    timestamps: true,
    versionKey: false,
  }
);

UserSchema.pre(/^find/, async function (next) {
  const user = this;
  user.find({ active: { $ne: false } });
  next();
});


const User = mongoose.model<IUser, UserModel>('User', UserSchema);

export default User;

在VSCode中,user.find({ active: { $ne: false } })这行出现类型错误:

Property 'find' does not exist on type '(Document<unknown, {}, FlatRecord<IUser>> & Omit<FlatRecord<IUser> & { _id: ObjectId; }, keyof IUserMethods> & IUserMethods) | Query<...>'.
  Property 'find' does not exist on type 'Document<unknown, {}, FlatRecord<IUser>> & Omit<FlatRecord<IUser> & { _id: ObjectId; }, keyof IUserMethods> & IUserMethods'.ts(2339)

问题根源

  1. 类型推断歧义:Mongoose的pre(/^find/)钩子中,this的类型被推断为Query | Document的联合类型。对于find类查询钩子,this应该是Query对象,但TypeScript无法确定具体类型,导致报错。
  2. 错误的方法调用:代码中试图在this上调用find(),但正确逻辑是修改当前查询的条件,而非调用新的find方法。

解决步骤

步骤1:明确this的类型

在钩子函数中,通过类型断言指定this为Mongoose的Query类型:

import { Query } from 'mongoose';

UserSchema.pre(/^find/, async function (this: Query<IUser[], IUser>, next) {
  this.find({ active: { $ne: false } });
  next();
});

步骤2:修正查询逻辑

直接在this(当前查询对象)上追加条件即可,无需将this赋值给user变量,避免类型混淆:

UserSchema.pre(/^find/, async function (this: Query<IUser[], IUser>, next) {
  this.find({ active: { $ne: false } });
  next();
});

关于Document<unknown>提示的解释

该提示是因为TypeScript无法确定联合类型中Document部分的具体泛型参数,当this被推断为Document | Query时,Document的泛型参数默认是unknown,导致类型提示模糊。通过明确this的类型为Query,即可消除这个模糊提示。

内容的提问来源于stack exchange,提问作者Vivek V Dwivedi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 09:02:51