I was having the same problem as you, and then finally managed to resolve it after reading the documentation in the TS mongoose typings (which I didn’t know about before, and I’m not sure how long the docs have been around), specifically this section.
As for your case, you’ll want to follow a similar pattern to what you currently have, although you’ll need to change a few things in both files.
IUser file
- Rename
IUsertoIUserDocument. This is to separate your schema from your instance methods. - Import
Documentfrom mongoose. - Extend the interface from
Document.
Model file
- Rename all instances of
IUsertoIUserDocument, including the module path if you rename the file. - Rename only the definition of
IUserModeltoIUser. - Change what
IUserextends from, fromIUserDocument, DocumenttoIUserDocument. - Create a new interface called
IUserModelwhich extends fromModel<IUser>. - Declare your static methods in
IUserModel. - Change the
Userconstant type fromModel<IUserModel>toIUserModel, asIUserModelnow extendsModel<IUser>. - Change the type argument on your model call from
<IUserModel>to<IUser, IUserModel>.
Here’s what your model file would look like with those changes:
import * as bcrypt from 'bcryptjs';
import { Document, Schema, Model, model } from 'mongoose';
import { IUserDocument } from '../interfaces/IUserDocument';
export interface IUser extends IUserDocument {
comparePassword(password: string): boolean;
}
export interface IUserModel extends Model<IUser> {
hashPassword(password: string): string;
}
export const userSchema: Schema = new Schema({
email: { type: String, index: { unique: true }, required: true },
name: { type: String, index: { unique: true }, required: true },
password: { type: String, required: true }
});
userSchema.method('comparePassword', function (password: string): boolean {
if (bcrypt.compareSync(password, this.password)) return true;
return false;
});
userSchema.static('hashPassword', (password: string): string => {
return bcrypt.hashSync(password);
});
export const User: IUserModel = model<IUser, IUserModel>('User', userSchema);
export default User;
And your (newly renamed) ../interfaces/IUserDocument module would look like this:
import { Document } from 'mongoose';
export interface IUserDocument extends Document {
email: string;
name: string;
password: string;
}