Typescript mongoose静态模型方法"属性不存在于类型上"

Clu*_*dch 25 mongoose node.js typescript

我目前正在尝试向我的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;
Run Code Online (Sandbox Code Playgroud)

IUSER:

export interface IUser {
    email: string;
    name: string;
    password: string;
}
Run Code Online (Sandbox Code Playgroud)

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

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

Har*_*ton 58

我遇到了和你一样的问题,然后在阅读了TS mongoose打字机的文档之后终于设法解决了它(之前我不知道,我不知道文档有多长时间了),特别是这一节.我知道这是一个非常古老的问题,但我认为它可能有助于将来失去一些人. 编辑:我在2014年3月读到现有答案的日期,而不是3月14日.


至于你的情况,你会希望遵循与你现有的类似的模式,虽然你需要在两个文件中改变一些东西.

IUser文件

  1. 重命名IUserIUserDocument.这是为了将您的架构与实例方法分开.
  2. Document从猫鼬进口.
  3. 扩展接口Document.

模型文件

  1. 如果重命名文件,则重命名IUserto的所有实例IUserDocument,包括模块路径.
  2. 重命名为IUserModelto 的定义IUser.
  3. 改变IUser从,延伸IUserDocument, Document到的IUserDocument.
  4. 创建一个名为IUserModelextends 的新接口Model<IUser>.
  5. 声明你的静态方法IUserModel.
  6. User常量类型更改Model<IUserModel>IUserModel,如IUserModel现在扩展Model<IUser>.
  7. 将模型调用的类型参数更改<IUserModel><IUser, IUserModel>.

以下是您的模型文件与这些更改的外观:

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;
Run Code Online (Sandbox Code Playgroud)

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

import { Document } from 'mongoose';

export interface IUserDocument extends Document {
    email: string;
    name: string;
    password: string;
}
Run Code Online (Sandbox Code Playgroud)


Nat*_*May 12

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

User.comparePassword(candidate, cb...)
Run Code Online (Sandbox Code Playgroud)

这不起作用,因为方法是在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);
          }
        })
      })
    })
);
Run Code Online (Sandbox Code Playgroud)

所以我findOne({})以前得到了文档实例,然后通过挖掘文档中的模式属性来访问模式方法 user.schema.methods.comparePassword

我发现了一些不同之处:

  1. 我是一种instance方法而你的static方法是一种方法.我有信心有类似的方法访问策略.
  2. 我发现我必须将哈希传递给comparePassword()函数.也许这对静力学没有必要,但我无法访问this.password

  • 我一直在努力解决这个问题.要通过Typescript访问静态模式方法,请使用`User.schema.statics.hashPassword()`. (4认同)

Max*_*der 5

对于未来的读者:

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

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

每个文档包含其自己的数据。每个实例还带有自己的实例方法,这些实例方法与自己的方法绑定在一起,this并且只能在该特定实例上运行。

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

这与TypeScript有何关系:

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

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

记住,当您在代码中使用这些东西时,该模型用于创建新的Documents并调用诸如User.findOne或您的自定义静态变量(User.hashPassword如上定义)之类的静态方法。

文档是您用来从对象访问特定数据或调用实例方法(如上面定义的)this.save和自定义实例方法(如this.comparePassword上面定义的)的工具。