with restful part nodejs node create apis and node.js typescript mongoose

node.js - restful - mongoose and typescript



Método del modelo estático de mongoose mecanografiado "La propiedad no existe en el tipo" (4)

Actualmente estoy intentando agregar un método estático a mi esquema de mangosta, pero no puedo encontrar la razón por la que no funciona de esta manera.

Mi modelo:

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

Si ahora trato de llamar a User.hashPassword(password) el siguiente error [ts] Property ''hashPassword'' does not exist on type ''Model<IUserModel>''.

Sé que no definí el método en ninguna parte, pero realmente no sé dónde podría ponerlo, ya que no puedo poner un método estático en una interfaz. Espero que puedan ayudarme a encontrar el error, gracias de antemano!


Creo que tienes el mismo problema con el que acabo de luchar. Este problema está en su llamada. Varios tutoriales le han llamado al método .comparePassword() del modelo como este.

User.comparePassword(candidate, cb...)

Esto no funciona porque el método está en el schema no en el model . La única forma en que pude llamar al método fue encontrar esta instancia del modelo utilizando los métodos de consulta estándar de mangosta / mongo.

Aquí está la parte relevante de mi middleware pasaporte:

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

Así que usé findOne({}) para obtener la instancia del documento y luego tuve que acceder a los métodos de esquema al user.schema.methods.comparePassword en las propiedades del esquema en el documento user.schema.methods.comparePassword

Un par de diferencias que noté:

  1. El mío es un método de instance , mientras que el tuyo es un método static . Estoy seguro de que hay una estrategia de acceso de método similar.
  2. Descubrí que tenía que pasar el hash a la función comparePassword() . tal vez esto no sea necesario en estadísticas, pero no this.password acceder a este. this.password

Estaba teniendo el mismo problema que usted y, finalmente, logré resolverlo después de leer la documentación en los manuscritos de la mangosta TS (que no conocía antes, y no estoy seguro de cuánto tiempo llevan los documentos), específicamente esta sección . Entiendo que esta es una pregunta muy antigua, pero pensé que podría ayudar a alguna persona perdida en el futuro. Edición: leí la fecha de una respuesta existente como marzo de 2014, no como el 14 de marzo.

En cuanto a su caso, querrá seguir un patrón similar al que tiene actualmente, aunque tendrá que cambiar algunas cosas en ambos archivos.

Archivo IUser

  1. Cambie el nombre de IUser a IUserDocument . Esto es para separar su esquema de sus métodos de instancia.
  2. Importar Document desde mangosta.
  3. Extender la interfaz desde el Document .

Archivo de modelo

  1. Cambie el nombre de todas las instancias de IUser a IUserDocument , incluida la ruta del módulo si cambia el nombre del archivo.
  2. Renombrar solo la definición de IUserModel a IUser .
  3. Cambie lo que IUser extiende, de IUserDocument, Document a IUserDocument .
  4. Cree una nueva interfaz llamada IUserModel que se extienda desde el Model<IUser> .
  5. Declara tus métodos estáticos en IUserModel .
  6. Cambie el tipo de constante de User del Model<IUserModel> a IUserModel , ya que IUserModel ahora amplía el Model<IUser> .
  7. Cambie el argumento de tipo en su llamada modelo de <IUserModel> a <IUser, IUserModel> .

Aquí es cómo se vería tu archivo modelo con esos cambios:

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;

Y su (recién renombrado) ../interfaces/IUserDocument módulo se vería así:

import { Document } from ''mongoose''; export interface IUserDocument extends Document { email: string; name: string; password: string; }


No puedo ver la interfaz de IUser, pero sospecho que no ha incluido los métodos allí. P.EJ

export interface IUser { email: string, hash: string, salt: string, setPassword(password: string): void, validPassword(password: string): boolean, generateJwt(): string }

mecanografiar reconocerá sus métodos y dejará de quejarse


Para futuros lectores:

Recuerde que estamos tratando con dos conceptos diferentes de Mongo / Mongoose: un modelo y documentos.

Se pueden crear muchos documentos a partir de un solo modelo. El Modelo es el plano, el Documento es lo creado de acuerdo con las instrucciones del Modelo.

Cada documento contiene sus propios datos. Cada uno también lleva sus propios métodos de instancia individual que están ligados a su propio this y solo operan en esa instancia específica.

El Modelo puede tener métodos ''estáticos'' que no están vinculados a una instancia de Documento específica, sino que operan en toda la colección de Documentos.

Cómo todo esto se relaciona con TypeScript:

  • Extienda el documento para definir tipos para propiedades de instancia y funciones .method .
  • Extienda el modelo (de un documento) para definir tipos para funciones .static .

Las otras respuestas aquí tienen código decente, así que mírelas y busque las diferencias entre cómo se definen los Documentos y cómo se definen los Modelos.

Y recuerde que cuando vaya a usar estas cosas en su código, el Modelo se usa para crear nuevos Documentos y para llamar a métodos estáticos como User.findOne o sus estadísticas personalizadas (como User.hashPassword se define arriba).

Y los documentos son lo que usa para acceder a los datos específicos del objeto, o para llamar a métodos de instancia como this.save y métodos de instancia personalizados como this.comparePassword definidos anteriormente.