node.js - Typescript mongoose metodă statică a modelului „Proprietatea nu există pe tip”
Încerc în prezent să adaug o metodă statică la schema mea mongoose, dar nu pot găsi motivul pentru care nu funcționează în acest fel.
Modelul meu:
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;
Utilizator :
export interface IUser {
email: string;
name: string;
password: string;
}
Dacă încerc acum să apelez User.hashPassword(password), primesc următoarea eroare [ts] Proprietatea 'hashPassword' nu există pe tipul 'Model<IUserModel>'.
Știu că nu am definit metoda nicăieri, dar nu știu exact unde aș putea să o pun, deoarece nu pot să pun pur și simplu o metodă statică într-o interfață. Sper că mă poți ajuta să găsesc eroarea, mulțumesc anticipat!
Răspuns #1
Am avut aceeași problemă ca tine și apoi am reușit în final să o rezolv după ce am citit documentația din tipurile TS pentru mongoose (despre care nu știam înainte și nu sunt sigur cât timp a existat documentația), în special această secțiune.
În ceea ce privește cazul tău, va trebui să urmezi un tipar similar cu ceea ce ai în prezent, deși va trebui să schimbi câteva lucruri în ambele fișiere.
Fișier utilizator
- Redenumește
IUserînIUserDocument. Acest lucru este pentru a separa schema ta de metodele instanței. - Importă
Documentdin mongoose. - Extinde interfața din
Document.
Fișier model
- Redenumește toate instanțele de
IUserînIUserDocument, inclusiv calea modulului dacă redenumești fișierul. - Redenumește doar definiția lui
IUserModelînIUser. - Schimbați ceea ce
IUserextinde, de laIUserDocument, DocumentlaIUserDocument. - Creează o nouă interfață numită
IUserModelcare extindeModel<IUser>. - Declară metodele tale statice în
IUserModel. - Schimbați tipul constantei
Userde laModel<IUserModel>laIUserModel, deoareceIUserModelextinde acumModel<IUser>. - Schimbați argumentul de tip la apelul modelului dvs. de la
<IUserModel>la<IUser, IUserModel>.
Iată cum ar arăta fișierul modelului tău cu aceste modificări:
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;
Și modulul tău (recent redenumit) ../interfaces/IUserDocument ar arăta astfel:
import { Document } from 'mongoose';
export interface IUserDocument extends Document {
email: string;
name: string;
password: string;
}
Răspunsul #2
Cred că ai aceeași problemă cu care m-am confruntat eu recent. Această problemă este în apelul tău. Mai multe tutoriale îți arată să apelezi metoda .comparePassword() din model astfel.
User.comparePassword(candidate, cb...)
Acest lucru nu funcționează deoarece metoda se află pe schema, nu pe model. Singura modalitate prin care am reușit să apelez metoda a fost găsind această instanță a modelului folosind metodele standard de interogare mongoose/mongo.
Iată partea relevantă din middleware-ul meu Passport:
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);
}
})
})
})
);
Așa că am folosit findOne({}) pentru a obține instanța documentului și apoi a trebuit să accesez metodele schemei prin explorarea proprietăților schemei pe document user.schema.methods.comparePassword
Câteva diferențe pe care le-am observat:
- Al meu este o metodă
instance, în timp ce al tău este o metodăstatic. Sunt încrezător că există o strategie similară de accesare a metodei. - Am descoperit că trebuia să transmit hash-ul funcției
comparePassword(). Poate că acest lucru nu este necesar pentru statics, dar nu am reușit să accesezthis.password
Răspunsul #3
Pentru cititorii viitori:
Amintiți-vă că avem de-a face cu două concepte diferite în Mongo/Mongoose: un Model și Documente.
Multe documente pot fi create dintr-un singur model. Modelul este planul, documentul este obiectul creat conform instrucțiunilor modelului.
Fiecare document conține propriile sale date. Fiecare poartă, de asemenea, propriile metode individuale de instanță care sunt legate de propriul său this și operează doar asupra acelei instanțe specifice.
Modelul poate avea metode 'statice' care nu sunt legate de o instanță specifică de Document, dar operează asupra întregii colecții de Documente.
Cum se leagă toate acestea de TypeScript:
- Extinde documentul pentru a defini tipurile pentru proprietățile instanței și funcțiile
.method. - Extindeți Modelul (al unui Document) pentru a defini tipuri pentru funcțiile
.static.
Celelalte răspunsuri de aici au cod destul de bun, așa că uită-te la ele și urmărește diferențele dintre modul în care sunt definite Documentele și modul în care sunt definite Modelele.
Și amintește-ți că atunci când folosești aceste lucruri în codul tău, Modelul este folosit pentru a crea Documente noi și pentru a apela metode statice precum User.findOne sau staticele tale personalizate (cum este definit mai sus User.hashPassword).
Și Documentele sunt ceea ce folosești pentru a accesa datele specifice din obiect sau pentru a apela metode de instanță precum this.save și metode de instanță personalizate precum this.comparePassword definite mai sus.
