打字稿mongoose静态模型方法"属性不存在于类型"

时间:2017-02-24 21:12:27

标签: node.js typescript mongoose

我目前正在尝试为我的mongoose架构添加一个静态方法,但我找不到它为什么不以这种方式工作的原因。

我的模特:

import * as bcrypt from 'bcryptjs';
import { Document, Schema, Model, model } from 'mongoose';

import { IUser } from '../interfaces/IUser';

export interface IUserModel extends IUser, Document {
    comparePassword(password: string): boolean;
}

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: Model<IUserModel> = model<IUserModel>('User', userSchema);

export default User;

IUSER:

export interface IUser {
    email: string;
    name: string;
    password: string;
}

如果我现在尝试拨打User.hashPassword(password),我收到以下错误[ts] Property 'hashPassword' does not exist on type 'Model<IUserModel>'.

我知道我没有在任何地方定义方法,但我不知道我可以把它放在哪里,因为我不能将静态方法放入接口。 我希望你能提前帮助我找到错误!

6 个答案:

答案 0 :(得分:39)

我遇到了和你一样的问题,然后在读完TS猫鼬类型的文件之后终于设法解决了它(之前我都不知道,我不知道多长时间文档已经存在,特别是this section我知道这是一个非常旧问题,但我认为它可能会帮助一些失去的人。 编辑:我读了现有答案的日期截至2014年3月,而不是3月14日。

至于您的情况,您希望遵循与您目前相似的模式,尽管您需要更改这两个文件中的一些内容。

IUser文件

  1. IUser重命名为IUserDocument。这是为了将您的架构与实例方法分开。
  2. 从mongoose导入Document
  3. Document
  4. 扩展界面

    模型文件

    1. IUser的所有实例重命名为IUserDocument,包括重命名文件时的模块路径。
    2. IUserModel仅定义重命名为IUser
    3. 更改IUserIUserDocument, Document扩展到IUserDocument的内容。
    4. 创建一个名为IUserModel的新界面,该界面从Model<IUser>延伸。
    5. IUserModel
    6. 中声明您的静态方法
    7. User常量类型从Model<IUserModel>更改为IUserModel,因为IUserModel现在扩展为Model<IUser>
    8. 将模型调用的类型参数从<IUserModel>更改为<IUser, IUserModel>
    9. 以下是这些更改后您的模型文件的样子:

      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): boolean;
      }
      
      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;
      

      你的(新重命名的)../interfaces/IUserDocument模块看起来像这样:

      import { Document } from 'mongoose';
      
      export interface IUserDocument extends Document {
          email: string;
          name: string;
          password: string;
      }
      

答案 1 :(得分:10)

我认为你遇到的问题与我刚刚遇到的问题相同。此问题在您的电话中。您可以通过几个教程从模型中调用.comparePassword()方法。

User.comparePassword(candidate, cb...)

这不起作用,因为该方法位于schema而不是model。我能够调用该方法的唯一方法是使用标准的mongoose / mongo查询方法找到该模型的实例。

以下是我的护照中间件的相关部分:

passport.use(
  new LocalStrategy({
    usernameField: 'email'
  },
    function (email: string, password: string, done: any) {
      User.findOne({ email: email }, function (err: Error, user: IUserModel) {
        if (err) throw err;
        if (!user) return done(null, false, { msg: 'unknown User' });
        user.schema.methods.comparePassword(password, user.password, function (error: Error, isMatch: boolean) {
          if (error) throw error;
          if (!isMatch) return done(null, false, { msg: 'Invalid password' });
          else {
            console.log('it was a match'); // lost my $HÏT when I saw it
            return done(null, user);
          }
        })
      })
    })
);

所以我使用findOne({})来获取文档实例,然后通过深入研究文档user.schema.methods.comparePassword

上的模式属性来访问模式方法

我注意到了一些不同之处:

  1. Mine是一种instance方法,而您的方法是static方法。我确信有类似的方法访问策略。
  2. 我发现我必须将哈希值传递给comparePassword()函数。也许这对于静力学来说并不是必需的,但我无法访问this.password

答案 2 :(得分:5)

对于将来的读者:

请记住,我们正在处理两种不同的Mongo / Mongoose概念:模型和文档。

可以从一个模型中创建许多文档。模型是蓝图,文档是根据模型的说明创建的事物。

每个文档包含其自己的数据。每个实例还带有自己的实例方法,这些实例方法绑定到自己的this上,并且仅在该特定实例上操作。

模型可以具有“静态”方法,这些方法不与特定的Document实例绑定,而是在整个Document集合上运行。

这与TypeScript有何关系:

  • 扩展文档以定义实例属性和.method函数的类型。
  • 扩展(文档的)模型以定义.static函数的类型。

这里的其他答案都有不错的代码,因此请仔细阅读并找出文档定义和模型定义之间的差异。

请记住,当您要在代码中使用这些内容时,该模型用于创建新文档并调用诸如User.findOne之类的静态方法或您的自定义静态方法(如以上定义的User.hashPassword之类)

然后使用文档来访问对象中的特定数据,或调用上面定义的实例方法(如this.save和自定义实例方法(如this.comparePassword)。

答案 3 :(得分:0)

我看不到你的IUser界面,但是我怀疑你没有在那里包含这些方法。 EG

export interface IUser {
    email: string,
    hash: string,
    salt: string,

    setPassword(password: string): void,
    validPassword(password: string): boolean,
    generateJwt(): string
}

typescript将识别您的方法并停止抱怨

答案 4 :(得分:0)

因此,我进行了70次更新,我也给予了赞成。但这不是一个完整的解决方案。他使用基于OP的简单示例。但是,当我们使用staticsmethods来扩展模型的功能时,通常要引用模型本身。解决方案的问题是他使用了回调函数,这意味着this的值将不会引用类上下文,而是会引用全局变量。

第一步是调用statics属性,而不是将该属性作为参数传递给static函数:

schema.statics.hashPassword

现在我们无法为该成员分配箭头功能,因为箭头功能内的this仍将引用全局对象!为了在模型的上下文中捕获this,我们必须使用函数表达式语法:

schema.statics.hashPassword = async function(password: string): Promise<string> {
    console.log('the number of users: ', await this.count({}));
    ...
}

答案 5 :(得分:0)

参考:https://mongoosejs.com/docs/typescript.html

  1. 只需在表示文档结构的架构之前创建一个接口即可。
  2. 将接口类型添加到模型中。
  3. 导出模型。

从猫鼬文档中引用以下内容:

import { Schema, model, connect } from 'mongoose';

// 1. Create an interface representing a document in MongoDB.
interface User {
  name: string;
  email: string;
  avatar?: string;
}

// 2. Create a Schema corresponding to the document interface.
const schema = new Schema<User>({
  name: { type: String, required: true },
  email: { type: String, required: true },
  avatar: String
});

// 3. Create a Model.
const UserModel = model<User>('User', schema);

如果您向架构添加任何方法,也请在接口中添加其定义。