打字稿和猫鼬:“文档”类型上不存在属性“x”

Posted

技术标签:

【中文标题】打字稿和猫鼬:“文档”类型上不存在属性“x”【英文标题】:Typescript and Mongoose: Property 'x' does not exist on type 'Document' 【发布时间】:2021-02-12 20:23:18 【问题描述】:

这是我与 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));
  
;

我收到以下错误:

“文档”类型上不存在属性“转换”。

“模型”类型上不存在属性“checkDuplicateEmailError”。

我尝试了export default mongoose.model<any>("User", userSchema);,但没有收到transform 错误,但仍然是checkDuplicateEmailError 的错误。

【问题讨论】:

这里有不少TS错误。我在transformed[field] 上看到红色下划线,我很惊讶err.code 上没有警告,因为errany。您需要一堆注释,包括将User 定义为具有transform() 方法。我会给你写一个答案,但我必须检查文档中的一些内容,因为我对 TS 的了解比对猫鼬的了解要多得多。 【参考方案1】:

您知道mongoose.model("User", userSchema); 创建了一个Model,但问题是:什么模型?

在没有任何类型注释的情况下,模型User 获取类型Model<Document, >,从new User() 创建的user 对象获取类型Document。因此,您当然会收到类似 “类型 'Document' 上不存在属性 'transform'。”之类的错误。

当您添加 <any> 变量时,user 的类型变为 any。这实际上给我们的信息比知道userDocument 要少。

我们要做的是为描述我们用户的特定类型的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]>

我们的UserModelUserDocumentModel,它还包括我们的checkDuplicateEmailError 函数。

interface UserModel extends Model<UserDocument> 
  checkDuplicateEmailError(err: any): any;

我们还应该在创建Schema 时添加UserDocument 泛型,这样当我们在架构方法中访问this 时,它的类型就会是UserDocument

const userSchema = new Schema<UserDocument>(

我在尝试实现 transform() 方法时遇到了各种打字错误,包括缺少索引签名。通过使用lodash 中的pick 方法,我们可以避免在这里重新发明***。我仍然对 mongoose 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 检查以避免运行时错误,如果errundefined,则尝试访问属性err.code

if ( typeof err === "object" && err.code === 11000) 

这应该可以解决您的所有错误。

Playground Link

【讨论】:

以上是关于打字稿和猫鼬:“文档”类型上不存在属性“x”的主要内容,如果未能解决你的问题,请参考以下文章

打字稿和正则表达式

打字稿和vue中的Summernote

使用猫鼬模型设置打字稿

解决打字稿错误的最佳方法 - 类型上不存在属性

打字稿:类型上不存在属性“导航”省略反应导航v5

TypeScript:“文档”类型上不存在属性“slug”