这是我与TypeScript一起使用的Mongoose模型:
import mongoose, { Schema } from "mongoose";
const userSchema: Schema = new Schema(
{
email: {
type: String,
required: true,
unique: true,
lowercase: true,
},
name: {
type: String,
maxlength: 50,
},
...
...
}
);
userSchema.method({
transform() {
const transformed = {};
const fields = ["id", "name", "email", "createdAt", "role"];
fields.forEach((field) => {
transformed[field] = this[field];
});
return transformed;
},
});
userSchema.statics = {
roles,
checkDuplicateEmailError(err: any) {
if (err.code === 11000) {
var error = new Error("Email already taken");
return error;
}
return err;
},
};
export default mongoose.model("User", userSchema);
我在控制器中使用此模型:
import { Request, Response, NextFunction } from "express";
import User from "../models/user.model";
import httpStatus from "http-status";
export const register = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
const user = new User(req.body);
const savedUser = await user.save();
res.status(httpStatus.CREATED);
res.send(savedUser.transform());
} catch (error) {
return next(User.checkDuplicateEmailError(error));
}
};
我收到以下错误:
类型“文档”上不存在属性“转换”。
类型'Model
'中不存在属性'checkDuplicateEmailError'。
我尝试了export default mongoose.model<any>("User", userSchema);
,但没有收到transform
错误,但仍然是checkDuplicateEmailError
的错误。
答案 0 :(得分:1)
您知道mongoose.model("User", userSchema);
创建了Model
,但问题是:什么模型?
没有任何类型注释,模型User
的类型为Model<Document, {}>
,从user
创建的new User()
对象的类型为Document
。因此,您当然会遇到类似“类型'Document'上不存在属性'transform'的错误。”
添加<any>
变量后,user
的类型变为any
。与知道user
是Document
相比,实际上给我们的信息更少。
我们想要做的是为描述用户的特定类型Document
创建一个模型。用户实例应具有方法transform()
,而模型本身应具有方法checkDuplicateEmailError()
。为此,我们将泛型传递给mongoose.model()
函数:
export default mongoose.model<UserDocument, UserModel>("User", userSchema);
困难的部分是找出这两种类型。令人沮丧的是,虽然there are packages that do this,猫鼬不会自动将架构中的字段用作类型的属性。因此,我们必须将它们写为打字稿类型。
interface UserDocument extends Document {
id: number;
name: string;
email: string;
createdAt: number;
role: string;
transform(): Transformed;
}
我们的transform
函数从UserDocument
返回具有5个特定属性的对象。为了访问这些属性的名称而不必再次键入它们,我将fields
方法内部的transform
移到了顶级属性中。我使用as const
来将它们的类型保留为字符串文字,而不仅仅是string
。 (typeof transformFields)[number]
给我们这些字符串的并集。
const transformFields = ["id", "name", "email", "createdAt", "role"] as const;
type Transformed = Pick<UserDocument, (typeof transformFields)[number]>
我们的UserModel
是Model
的{{1}},它还包含我们的UserDocument
函数。
checkDuplicateEmailError
在创建interface UserModel extends Model<UserDocument> {
checkDuplicateEmailError(err: any): any;
}
时,我们还应该添加UserDocument
泛型,以便在我们通过模式方法访问Schema
时,this
的类型为UserDocument
。 / p>
const userSchema = new Schema<UserDocument>({
尝试实现transform()
方法时遇到各种打字错误,包括缺少索引签名。通过使用pick
中的lodash
方法,我们可以避免在这里重新发明轮子。我仍然遇到猫鼬methods()
辅助函数的问题,但是使用直接分配方法可以正常工作。
userSchema.methods.transform = function (): Transformed {
return pick(this, transformFields);
};
您还可以使用解构来避免索引签名问题。
userSchema.methods.transform = function (): Transformed {
const {id, name, email, createdAt, role} = this;
return {id, name, email, createdAt, role};
}
在您的电子邮件检查功能中,我添加了typeof
检查,以避免在err.code
为err
的情况下尝试访问属性undefined
的运行时错误。
if ( typeof err === "object" && err.code === 11000) {
那应该可以解决所有错误。