Tìm kiếm trang web

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

  1. Đổi tên IUser thành IUserDocument. Việc này nhằm tách biệt schema của bạn khỏi các phương thức instance.
  2. Nhập Document từ mongoose.
  3. Mở rộng giao diện từ Document.

Tệp mô hình

  1. Đổi tên tất cả các trường hợp của IUser thành IUserDocument, bao gồm cả đường dẫn module nếu bạn đổi tên tệp.
  2. Đổi tên chỉ phần định nghĩa của IUserModel thành IUser.
  3. Thay đổi phần IUser mở rộng từ IUserDocument, Document thành IUserDocument.
  4. Tạo một giao diện mới gọi là IUserModel mà kế thừa từ Model<IUser>.
  5. Khai báo các phương thức tĩnh của bạn trong IUserModel.
  6. Thay đổi kiểu hằng User từ Model<IUserModel> thành IUserModel, vì IUserModel hiện nay kế thừa Model<IUser>.
  7. 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:

  1. Của tôi là một phương thức instance trong khi của bạn là một phương thức static. Tôi tin rằng có một chiến lược truy cập phương thức tương tự.
  2. 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ập this.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.

Bài viết liên quan