Reputation: 137
I try to create method hashPassword for user schema.
schema.method("hashPassword", function (): void {
const salt = bcrypt.genSaltSync(10);
const hash = bcrypt.hashSync(this.password, salt);
this.password = hash;
});
And get an error Property 'password' does not exist on type 'Document<any>'.
on password
Here is my file
import mongoose, { Schema, Document } from "mongoose";
import bcrypt from "bcryptjs";
/**
* This interface should be the same as JWTPayload declared in types/global.d.ts file
*/
export interface IUser extends Document {
name: string;
email: string;
username: string;
password: string;
confirmed: boolean;
hashPassword: () => void;
checkPassword: (password: string) => boolean;
}
// User schema
const schema = new Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
username: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
confirmed: {
type: Boolean,
default: false,
},
},
{ timestamps: true }
);
schema.method("hashPassword", function (): void {
const salt = bcrypt.genSaltSync(10);
const hash = bcrypt.hashSync(this.password, salt);
this.password = hash;
});
// User model
export const User = mongoose.model<IUser>("User", schema, "users");
Upvotes: 8
Views: 9907
Reputation: 11
Solution to avoid having to redefine types that are already in the schema
import { Schema, model, InferSchemaType } from "mongoose"
import bcrypt from "bcrypt"
const userSchema = new Schema({
name: String,
email: {
type: String,
required: true,
unique: true
},
password: String,
})
userSchema.methods.verifyPassword = async function(password: string){
return await bcrypt.compare(password, this.password)
}
declare interface IUser extends InferSchemaType<typeof userSchema> {
verifyPassword(password: string): boolean
}
export const UserModel = model<IUser>("Users", userSchema)
Upvotes: 1
Reputation: 1178
This makes is Clear Enough
import mongoose, { Schema, Document, Model } from "mongoose";
import bcrypt from "bcrypt";
interface IUser {
username: string;
hashedPassword: string;
}
interface IUserDocument extends IUser, Document {
setPassword: (password: string) => Promise<void>;
checkPassword: (password: string) => Promise<boolean>;
}
interface IUserModel extends Model<IUserDocument> {
findByUsername: (username: string) => Promise<IUserDocument>;
}
const UserSchema: Schema<IUserDocument> = new Schema({
username: { type: String, required: true },
hashedPassword: { type: String, required: true },
});
UserSchema.methods.setPassword = async function (password: string) {
const hash = await bcrypt.hash(password, 10);
this.hashedPassword = hash;
};
UserSchema.methods.checkPassword = async function (password: string) {
const result = await bcrypt.compare(password, this.hashedPassword);
return result;
};
UserSchema.statics.findByUsername = function (username: string) {
return this.findOne({ username });
};
const User = mongoose.model<IUserDocument, IUserModel>("User", UserSchema);
export default User;
Upvotes: 5
Reputation: 300
As suggested by one of the collaborators of mongoose, we can use the below way for creating instance methods:
const schema = new Schema<ITestModel, Model<ITestModel, {}, InstanceMethods>> // InstanceMethods would be the interface on which we would define the methods
schema.methods.methodName = function() {}
const Model = model<ITestModel, Model<ITestModel, {}, InstanceMethods>>("testModel", ModelSchema)
const modelInstance = new Model();
modelInstance.methodName() // works
link: https://github.com/Automattic/mongoose/issues/10358#issuecomment-861779692
Upvotes: 6
Reputation: 1012
You should declare an interface that extends Model like so:
interface IUser {...}
interface IUserInstanceCreation extends Model<IUser> {}
then declare your schema;
const userSchema = new Schema<IUser, IUserInstanceCreation, IUser>({...})
This would as well ensure that the Schema follows the attributes in IUser.
Upvotes: 2
Reputation: 42160
At the point where you define the method, the schema
object doesn't know that it is the Schema
for an IUser
and not just any Document
. You need to set the generic type for the Schema
when you create it: new Schema<IUser>( ... )
.
Upvotes: 10