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é:
- El mío es un método de
instance
, mientras que el tuyo es un métodostatic
. Estoy seguro de que hay una estrategia de acceso de método similar. - Descubrí que tenía que pasar el hash a la función
comparePassword()
. tal vez esto no sea necesario en estadísticas, pero nothis.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
- Cambie el nombre de
IUser
aIUserDocument
. Esto es para separar su esquema de sus métodos de instancia. - Importar
Document
desde mangosta. - Extender la interfaz desde el
Document
.
Archivo de modelo
- Cambie el nombre de todas las instancias de
IUser
aIUserDocument
, incluida la ruta del módulo si cambia el nombre del archivo. - Renombrar solo la definición de
IUserModel
aIUser
. - Cambie lo que
IUser
extiende, deIUserDocument, Document
aIUserDocument
. - Cree una nueva interfaz llamada
IUserModel
que se extienda desde elModel<IUser>
. - Declara tus métodos estáticos en
IUserModel
. - Cambie el tipo de constante de
User
delModel<IUserModel>
aIUserModel
, ya queIUserModel
ahora amplía elModel<IUser>
. - 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.