node.js - Phương thức model tĩnh mongoose trong Typescript "Thuộc tính không tồn tại trên kiểu"
Hiện tại tôi đang cố gắng thêm một phương thức tĩnh vào sơ đồ mongoose của mình nhưng tôi không thể tìm ra lý do tại sao nó không hoạt động theo cách này.
Mẫu của tôi:
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;
Người dùng :
export interface IUser {
email: string;
name: string;
password: string;
}
Nếu bây giờ tôi cố gắng gọi User.hashPassword(password) tôi nhận được lỗi sau [ts] Property 'hashPassword' không tồn tại trên kiểu 'Model
Tôi biết rằng tôi đã không định nghĩa phương thức ở bất kỳ đâu nhưng tôi thực sự không biết tôi có thể đặt nó ở đâu vì tôi không thể chỉ đặt một phương thức tĩnh vào trong một interface. Tôi hy vọng bạn có thể giúp tôi tìm lỗi, cảm ơn trước!
Câu trả lời #1
Tôi đã gặp cùng một vấn đề như bạn, và rồi cuối cùng đã giải quyết được sau khi đọc tài liệu trong các kiểu định nghĩa TS của mongoose (mà trước đây tôi không biết, và tôi cũng không chắc tài liệu này đã tồn tại bao lâu), cụ thể là phần này.
Đối với trường hợp của bạn, bạn sẽ muốn theo một mô hình tương tự như những gì bạn đang có, mặc dù bạn sẽ cần thay đổi một vài thứ trong cả hai tệp.
Tệp IUser
- Đổi tên
IUserthànhIUserDocument. Việc này nhằm tách biệt schema của bạn khỏi các phương thức instance. - Nhập
Documenttừ mongoose. - Mở rộng giao diện từ
Document.
Tệp mô hình
- Đổi tên tất cả các trường hợp của
IUserthànhIUserDocument, bao gồm cả đường dẫn module nếu bạn đổi tên tệp. - Đổi tên chỉ phần định nghĩa của
IUserModelthànhIUser. - Thay đổi phần
IUsermở rộng từIUserDocument, DocumentthànhIUserDocument. - Tạo một giao diện mới gọi là
IUserModelmà kế thừa từModel<IUser>. - Khai báo các phương thức tĩnh của bạn trong
IUserModel. - Thay đổi kiểu hằng
UsertừModel<IUserModel>thànhIUserModel, vìIUserModelhiện nay kế thừaModel<IUser>. - Thay đổi đối số kiểu trong cuộc gọi mô hình của bạn từ
<IUserModel>thành<IUser, IUserModel>.
Đây là cách tệp mô hình của bạn sẽ trông như thế nào với những thay đổi đó:
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;
Và module ../interfaces/IUserDocument (vừa được đổi tên) của bạn sẽ trông như sau:
import { Document } from 'mongoose';
export interface IUserDocument extends Document {
email: string;
name: string;
password: string;
}
Câu trả lời #2
Tôi nghĩ bạn đang gặp phải cùng một vấn đề mà tôi vừa gặp phải. Vấn đề này nằm trong lời gọi của bạn. Một số hướng dẫn đã yêu cầu bạn gọi phương thức .comparePassword() từ model như thế này.
User.comparePassword(candidate, cb...)
Điều này không hoạt động vì phương thức nằm trên schema chứ không phải trên model. Cách duy nhất tôi có thể gọi phương thức là bằng cách tìm thể hiện của model này sử dụng các phương thức truy vấn chuẩn của mongoose/mongo.
Đây là phần liên quan của middleware hộ chiếu của tôi:
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);
}
})
})
})
);
Vì vậy tôi đã sử dụng findOne({}) để lấy thể hiện tài liệu và sau đó phải truy cập các phương thức của schema bằng cách lục vào các thuộc tính schema trên tài liệu user.schema.methods.comparePassword
Một vài điểm khác biệt mà tôi đã nhận thấy:
- Của tôi là một phương thức
instancetrong khi của bạn là một phương thứcstatic. Tôi tin rằng có một chiến lược truy cập phương thức tương tự. - Tôi phát hiện ra rằng tôi phải truyền hash vào hàm
comparePassword(). có lẽ điều này không cần thiết trên các static, nhưng tôi không thể truy cậpthis.password
Câu trả lời #3
Dành cho những người đọc trong tương lai:
Hãy nhớ rằng chúng ta đang làm việc với hai khái niệm khác nhau của Mongo/Mongoose: một Model và các Document.
Nhiều Tài liệu có thể được tạo ra từ một Mô hình duy nhất. Mô hình là bản thiết kế, Tài liệu là thứ được tạo ra theo các hướng dẫn của Mô hình.
Mỗi Tài liệu chứa dữ liệu riêng của nó. Mỗi tài liệu cũng mang theo các phương thức thể hiện cá nhân riêng của chúng, được gắn với this của chính nó và chỉ hoạt động trên đúng một thể hiện cụ thể đó.
Mô hình có thể có các phương thức 'tĩnh' không gắn với một thể hiện Tài liệu cụ thể nào, nhưng hoạt động trên toàn bộ tập hợp các Tài liệu.
Tất cả điều này liên quan đến TypeScript như thế nào:
- Mở rộng Document để định nghĩa các loại cho thuộc tính của instance và các hàm
.method. - Mở rộng Mô hình (của một Tài liệu) để định nghĩa các kiểu cho các hàm
.static.
Các câu trả lời khác ở đây có mã khá tốt, vì vậy hãy xem chúng và theo dõi sự khác biệt giữa cách định nghĩa Documents và cách định nghĩa Models.
Và hãy nhớ rằng khi bạn sử dụng những thứ này trong mã của mình, Model được sử dụng để tạo các Document mới và để gọi các phương thức tĩnh như User.findOne hoặc các phương thức tĩnh tùy chỉnh của bạn (như User.hashPassword được định nghĩa ở trên).
Và Documents là những gì bạn sử dụng để truy cập dữ liệu cụ thể từ đối tượng, hoặc để gọi các phương thức của phiên bản như this.save và các phương thức của phiên bản tùy chỉnh như this.comparePassword đã được định nghĩa ở trên.
