我目前正在尝试为我的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>'.
我知道我没有在任何地方定义方法,但我不知道我可以把它放在哪里,因为我不能将静态方法放入接口。 我希望你能提前帮助我找到错误!
答案 0 :(得分:39)
我遇到了和你一样的问题,然后在读完TS猫鼬类型的文件之后终于设法解决了它(之前我都不知道,我不知道多长时间文档已经存在,特别是this section。 我知道这是一个非常旧问题,但我认为它可能会帮助一些失去的人。 编辑:我读了现有答案的日期截至2014年3月,而不是3月14日。
至于您的情况,您希望遵循与您目前相似的模式,尽管您需要更改这两个文件中的一些内容。
IUser文件
IUser
重命名为IUserDocument
。这是为了将您的架构与实例方法分开。Document
。Document
。模型文件
IUser
的所有实例重命名为IUserDocument
,包括重命名文件时的模块路径。IUserModel
的仅定义重命名为IUser
。IUser
从IUserDocument, Document
扩展到IUserDocument
的内容。IUserModel
的新界面,该界面从Model<IUser>
延伸。IUserModel
。User
常量类型从Model<IUserModel>
更改为IUserModel
,因为IUserModel
现在扩展为Model<IUser>
。<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): 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
我注意到了一些不同之处:
instance
方法,而您的方法是static
方法。我确信有类似的方法访问策略。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的简单示例。但是,当我们使用statics
和methods
来扩展模型的功能时,通常要引用模型本身。解决方案的问题是他使用了回调函数,这意味着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
从猫鼬文档中引用以下内容:
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);
如果您向架构添加任何方法,也请在接口中添加其定义。