mongoose 属性 'password' 在类型 'Document<any> 上不存在

问题描述

-- userSchema.ts 接口

import mongoose,{ Schema,Document } from "mongoose";
import moment from "moment";
import bcrypt from "bcrypt";

export interface UserDoc extends Document {
  name: {
    type: string;
    required: boolean;
  };
  email: {
    type: string;
    required: boolean;
  };
  password: {
    type: string;
    required: boolean;
  };
  dateJoined: {
    type: string;
    default: string;
  };
}

const userSchema = new Schema({
  name: {
    type: String,required: true,},email: {
    type: String,password: {
    type: String,dateJoined: {
    type: String,default: moment().format("MMMM Do YYYY"),});

我创建了我的用户模型,我遇到的问题是使用 bcrypt 创建 ma​​tchPassword 方法来比较 enteredPassword 参数与数据库中的密码>

userSchema.methods.matchPassword = async function (enteredPassword) {
  return await bcrypt.compare(enteredPassword,this.password); ***
};

userSchema.pre("save",async function (next) {
  if (this.isModified("password")) {
    next();
  }

  const salt = bcrypt.genSalt(10);

  *** this.password = await bcrypt.hash(this.password,await salt); ***
});

const User = mongoose.model("User",userSchema);

错误信息如下:

Property 'password' does not exist on type 'Document<any>'.

这个错误出现在 this.password 的每个实例上,由 ***

突出显示

我之前在 Javascript 中使用过相同的方法,所以我不知道为什么它不适用于打字稿以及如何将 this.password 绑定到 Mongoose 文档

谢谢

解决方法

看起来@Mohammad 已经帮助您实现了 bcrypt。我可以帮你解决打字稿错误!

UserDoc 是一个打字稿接口,所以它不应该有像 required 这样的字段。它应该只描述 UserDoc 对象的类型。默认情况下需要属性。如果它们是可选的,我们使用 ?:,但看起来这里都是必需的。

export interface UserDoc extends Document {
  name: string;
  email: string;
  password: string;
  dateJoined: string;
  matchPassword: (pw: string) => Promise<boolean>
}

当您创建 userSchema 时,您通过设置 UserDoc 构造函数的通用变量告诉打字稿这是 Document 的架构 - 不仅仅是任何 SchemaUserDoc

const userSchema = new Schema<UserDoc>({ ...

这清除了 userSchema.methods.matchPassword 中的错误,因为我们知道 this.password 是一个 string。我们还知道 enteredPasswordstring,因为我们在 matchPassword 接口中定义了此 UserDoc 方法的参数。

出于某种原因,pre 方法无法自动识别我们的文档类型。但是 pre 方法本身是一个通用函数,因此我们可以再次指定我们的文档是一个 UserDoc

userSchema.pre<UserDoc>( ...

这很愚蠢,但我们必须在创建模型时再次指定泛型。

const User = mongoose.model<UserDoc>("User",userSchema);

现在 User 的类型为 mongoose.Model<UserDoc,{}>,您调用的任何方法都应返回 UserDoc 而不仅仅是 Document

,

不要使用 await 作为回报,所以尝试

userSchema.methods.matchPassword = async function (enteredPassword) {
   let isValid = await bcrypt.compare(enteredPassword,this.password);
   return isValid 
};

并且你不需要在 bcrypt 中加盐,之后第二个参数应该是一个数字

this.password = await bcrypt.hash(this.password,12);

基于 documentation 如果你想使用盐,你可以这样做

const bcrypt = require('bcrypt');
const saltRounds = 10;
const myPlaintextPassword = 's0/\/\P4$$w0rD';
const someOtherPlaintextPassword = 'not_bacon';


const salt = bcrypt.genSaltSync(saltRounds);
const hash = bcrypt.hashSync(myPlaintextPassword,salt);