typescript 打字稿猫鼬静态模型方法“属性不存在于类型”

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/42448372/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 04:17:44  来源:igfitidea点击:

Typescript mongoose static model method "Property does not exist on type"

node.jstypescriptmongoose

提问by Cludch

I am currently trying to add a static method to my mongoose schema but I can't find the reason why it doesn't work this way.

我目前正在尝试向我的猫鼬模式添加一个静态方法,但我找不到它不能以这种方式工作的原因。

My model:

我的型号:

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;
}

If I now try to call User.hashPassword(password)I am getting the following error [ts] Property 'hashPassword' does not exist on type 'Model<IUserModel>'.

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

I know that I didn't define the method anywhere but I don't really know where I could put it as I can't just put a static method into an interface. I hope you can help my find the error, thanks in advance!

我知道我没有在任何地方定义方法,但我真的不知道我可以把它放在哪里,因为我不能把一个静态方法放到一个接口中。我希望你能帮助我找到错误,在此先感谢!

采纳答案by Nate May

I think you are having the same issue that I just struggled with. This issue is in your call. Several tutorials have you call the .comparePassword()method from the model like this.

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

User.comparePassword(candidate, cb...)

This doesn't work because the method is is on the schemanot on the model. The only way I was able to call the method was by finding this instance of the model using the standard mongoose/mongo query methods.

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

Here is relevant part of my passport middleware:

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

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

So I used findOne({})to get the document instance and then had to access the schema methods by digging into the schema properties on the document user.schema.methods.comparePassword

所以我曾经findOne({})获取文档实例,然后不得不通过深入研究文档上的架构属性来访问架构方法 user.schema.methods.comparePassword

A couple of differences that I noticed:

我注意到的一些差异:

  1. Mine is an instancemethod while yours is a staticmethod. I'm confident that there is a similar method access strategy.
  2. I found that I had to pass the hash to the comparePassword()function. perhaps this isn't necessary on statics, but I was unable to access this.password
  1. 我的是一种instance方法,而你的是一种static方法。我相信有类似的方法访问策略。
  2. 我发现我必须将哈希值传递给comparePassword()函数。也许这对静态来说不是必需的,但我无法访问this.password

回答by Matt Shipton

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.

我遇到了和你一样的问题,然后在阅读了 TS mongoose 类型中的文档后终于设法解决了它(我之前不知道,我不确定文档已经存在多久了),特别是这部分



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

用户文件

  1. Rename IUserto IUserDocument. This is to separate your schema from your instance methods.
  2. Import Documentfrom mongoose.
  3. Extend the interface from Document.
  1. 重命名IUserIUserDocument. 这是为了将您的架构与您的实例方法分开。
  2. Document从猫鼬进口。
  3. Document.

Model file

模型文件

  1. Rename all instances of IUserto IUserDocument, including the module path if you rename the file.
  2. Rename only the definitionof IUserModelto IUser.
  3. Change what IUserextends from, from IUserDocument, Documentto IUserDocument.
  4. Create a new interface called IUserModelwhich extends from Model<IUser>.
  5. Declare your static methods in IUserModel.
  6. Change the Userconstant type from Model<IUserModel>to IUserModel, as IUserModelnow extends Model<IUser>.
  7. Change the type argument on your model call from <IUserModel>to <IUser, IUserModel>.
  1. 重命名IUserto 的所有实例,IUserDocument如果重命名文件,则包括模块路径。
  2. 重命名只定义IUserModelIUser
  3. 改变什么IUser延伸自,从IUserDocument, DocumentIUserDocument
  4. 创建一个名为的新接口IUserModel,它从Model<IUser>.
  5. IUserModel.
  6. User常量类型从更改Model<IUserModel>IUserModel,就像IUserModel现在扩展一样Model<IUser>
  7. 将模型调用的类型参数从 更改<IUserModel><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/IUserDocumentmodule would look like this:

您的(新重命名的)../interfaces/IUserDocument模块如下所示:

import { Document } from 'mongoose';

export interface IUserDocument extends Document {
    email: string;
    name: string;
    password: string;
}

回答by Max Wilder

For future readers:

对于未来的读者:

Remember that we are dealing with two different Mongo/Mongoose concepts: a Model, and Documents.

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

Many Documents can be created from a single Model. The Model is the blueprint, the Document is the thing created according to the Model's instructions.

可以从单个模型创建许多文档。模型是蓝图,文档是根据模型的指令创建的东西。

Each Document contains its own data. Each also carries their own individual instance methods which are tied to its own thisand only operate on that one specific instance.

每个文档都包含自己的数据。每个还带有自己的单独实例方法,这些方法与其自身相关联,this并且仅在该特定实例上运行。

The Model can have 'static' methods which are not tied to a specific Document instance, but operate over the whole collection of Documents.

模型可以有“静态”方法,这些方法不绑定到特定的 Document 实例,而是对整个 Documents 集合进行操作。

How this all relates to TypeScript:

这一切与 TypeScript 有何关系:

  • Extend Document to define types for instance properties and .methodfunctions.
  • Extend the Model (of a Document) to define types for .staticfunctions.
  • 扩展 Document 以定义实例属性和.method函数的类型。
  • 扩展(文档的)模型以定义.static函数的类型。

The other answers here have decent code, so look at them and trace through the differences between how Documents are defined and how Models are defined.

此处的其他答案有不错的代码,因此请查看它们并跟踪文档定义方式与模型定义方式之间的差异。

And remember when you go to use these things in your code, the Model is used to create new Documents and to call static methods like User.findOneor your custom statics (like User.hashPasswordis defined above).

请记住,当您在代码中使用这些东西时,模型用于创建新文档并调用静态方法,如User.findOne或您的自定义静态(如User.hashPassword上面定义的)。

And Documents are what you use to access the specific data from the object, or to call instance methods like this.saveand custom instance methods like this.comparePassworddefined above.

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

回答by xerotolerant

I cannot see your IUser interface however I suspect that you have not included the methods in there. EG

我看不到您的 IUser 界面,但是我怀疑您没有在其中包含这些方法。例如

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

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

typescript will then recognize your methods and stop complaining

然后打字稿会识别你的方法并停止抱怨

回答by Donato

So the one with 70 updates I also gave an upvote. But it is not a complete solution. He uses a trivial example based on the OP. However, more often than not when we use staticsand methodsin order to extend the functionality of the model, we want to reference the model itself. The problem with his solution is he using a callback function which means the value of thiswill not refer to the class context but rather a global.

所以有 70 个更新的那个我也投了赞成票。但这不是一个完整的解决方案。他使用了一个基于 OP 的简单示例。然而,当我们使用statics并且methods为了扩展模型的功能时,通常我们想要引用模型本身。他的解决方案的问题是他使用了回调函数,这意味着 的值this不会引用类上下文,而是引用全局。

The first step is to invoke the staticsproperty rather than pass the property as an argument to the staticfunction:

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

schema.statics.hashPassword

Now we cannot assign an arrow function to this member, for thisinside the arrow function will still refer to the global object! We have to use function expression syntax in order to capture thisin the context of the model:

现在我们不能为这个成员分配箭头函数,因为this在箭头函数内部仍然会引用全局对象!我们必须使用函数表达式语法才能this在模型的上下文中捕获:

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