When using Typescript with Mongoose, you may encounter the error message "Property 'x' does not exist on type 'Document'"

Here is my custom Mongoose model used in conjunction with TypeScript:

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

This model is utilized in the controller as follows:

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

The following errors have been encountered:

Property 'transform' does not exist on type 'Document'.

Property 'checkDuplicateEmailError' does not exist on type 'Model<Document, {}>'.

Attempts have been made like

export default mongoose.model<any>("User", userSchema);
to resolve the transform issue, but the error for checkDuplicateEmailError still persists.

Answer №1

Have you ever wondered what exactly

mongoose.model("User", userSchema);
creates - is it just a model or something more?

When type annotations are absent, the User model is assigned the type Model<Document, {}>, while the user object created from new User() is of type Document. This can lead to errors like "Property 'transform' does not exist on type 'Document'."

By adding a <any> variable, the type of user becomes any, providing less information than knowing it is a Document.

To define a model for a specific type of Document that describes our user, we pass generics to the mongoose.model() function:

export default mongoose.model<UserDocument, UserModel>("User", userSchema);

However, determining these two types can be challenging as mongoose does not automatically map schema fields to type properties. Therefore, we need to define them as TypeScript types.

interface UserDocument extends Document {
  id: number;
  name: string;
  email: string;
  createdAt: number;
  role: string;
  transform(): Transformed;
}

The transform function returns an object with specific properties from the UserDocument. To simplify access to these properties, we use as const to keep their types as string literals.

const transformFields = ["id", "name", "email", "createdAt", "role"] as const;

type Transformed = Pick<UserDocument, (typeof transformFields)[number]>

The UserModel is a Model of UserDocument and includes the checkDuplicateEmailError function.

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

Ensure to add the UserDocument generic when creating the Schema to maintain type information.

const userSchema = new Schema<UserDocument>({

Implementing the transform() method can be tricky due to missing index signatures. Utilize the pick method from lodash to avoid reinventing the wheel.

userSchema.methods.transform = function (): Transformed {
  return pick(this, transformFields);
};

Alternatively, use destructuring to bypass index signature issues.

userSchema.methods.transform = function (): Transformed {
  const {id, name, email, createdAt, role} = this;
  return {id, name, email, createdAt, role};
}

In the email check function, incorporate a typeof check to prevent runtime errors.

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

Following these steps should resolve your errors.

Access Playground Here

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How can I dynamically apply an active class when clicking on a group of buttons?

Iterating through the buttons array, I retrieve three buttons. My goal is to determine which button has been clicked and apply an active class to it. import React from "react"; import { useState } from "react"; import style from "./years.module.scss"; co ...

Add a PHP variable to a JavaScript array

Looking for assistance with pushing a PHP variable to JavaScript. Below is the code that I have tried, but it doesn't seem to work. Can someone please guide me on how to achieve this? ticks.push(<?php echo json_encode($new); ?>); ...

Can you explain the functionality of that snippet of JavaScript code?

Can you figure out the value of r? How does it relate to Boolean operators and objects? var data = {x:123, y:456}; var r = data && data.x || 0; Update: Upon running the code snippet, I noticed that r consistently equals x. However, the reason behind thi ...

Tips for sending arguments up in Angular2

I need to supply an argument to a function. <select class="chooseWatchlist" (change)="updateWatchlistTable(item.name)"> <option *ngFor="let item of _items">{{ item.name }}</option> </select> It's crucial for me, as I have ...

Generating GraphQL Apollo types in React Native

I am currently using: Neo4J version 4.2 Apollo server GraphQL and @neo4j/graphql (to auto-generate Neo4J types from schema.graphql) Expo React Native with Apollo Client TypeScript I wanted to create TypeScript types for my GraphQL queries by following th ...

Adjust the pagination page size based on the value in the Angular scope dynamically

My goal is to provide the user with the ability to adjust the number of rows displayed in a table. This feature is crucial for our web app, as it needs to be accessible on various devices and should allow users to customize their viewing experience to redu ...

Storing binary data uploaded via AJAX in PHP on the server is essential for maintaining

I successfully imported a .png image file as an Array Buffer. var readSingleFile = function(e) { var file = e.target.files[0]; if (!file) { return; } var reader = new FileReader(); ...

Tips for sending code confirmation in Amazon Cognito identity using nest.js

Having some issues with implementing AWS Cognito login in Nest.js? Check out this helpful guide on token validation: https://medium.com/weekly-webtips/token-validation-with-aws-cognito-and-nestjs-6f9e4088393c. I need to add a feature where users receive ...

What are the steps for designing personalized syncfusion grid-columns while still preserving the built-in search functionality of syncfusion?

Looking to transform the data coming from the backend, specifically mapping a user's status which is represented as a number to its corresponding string value. Considered using typescript for this mapping task, but it interferes with syncfusion' ...

The React Vite application encountered an issue: There is no loader configured for ".html" files at ../server/node_modules/@mapbox/node-pre-gyp/lib/util/nw-pre-gyp/index.html

**Encountered errors in a React Vite web app** ** ✘ [ERROR] No loader is configured for ".html" files: ../server/node_modules/@mapbox/node-pre-gyp/lib/util/nw-pre-gyp/index.html ../server/node_modules/@mapbox/node-pre-gyp/lib/node-pre-gyp.js:86 ...

Avoiding code execution by injections in Javascript/Jquery

Currently, I'm fetching JSON data for a .getJSON function in Jquery. To ensure the data's security, I am considering using .text (I believe this is the correct approach). The JSON has been successfully validated. Below is the script that I am cu ...

Deactivate DropDownList within Update Panel with JQuery

Below is the Update Panel that I am working on: <asp:ScriptManager ID="ScriptManager1" runat="server" /> <asp:UpdatePanel ID="UpdatePanel1" UpdateMode="Conditional" runat="server"> <Triggers> <asp:AsyncPostBackTrigger ControlID="d ...

The added class is not being successfully applied to the ClassList

I'm currently working on a page where I want the colors of a button and the background to switch when the button is clicked. document.querySelector("body.light").classList.toggle("dark"); document.querySelector("button.dark").classList.toggle("light" ...

Is it possible to adjust the color of the iOS status bar using NativeScript, Angular 2, and TypeScript?

I recently came across this npm package called NativeScript Status Bar, available at the following link: https://www.npmjs.com/package/nativescript-statusbar However, I'm facing an issue because I cannot use a Page element as I am working with Angul ...

Exploring the concept of promise.reject() and the art of passing an object as a parameter

By passing response.json in Promise.reject(), I intended for it to be accessible in the catch block. However, it appears as undefined in the catch block. const checkResponse = (response) => { if (response.status >= 200 && response.status & ...

Video tag with centered image

For a current project, I am in need of rendering a centered image (a play button) at runtime on top of a video based on the UserAgent. If the userAgent is not Firefox, I want to display the image as Firefox has its own playEvent and button on top of the vi ...

When incorporating reduce into your code, remember to expect an undefined

Imagine you have an array like this: const novels = [ { id: 1, name: 'The Da Vinci Code', genre: 'Mystery', author: { name: 'Dan Brown', birthYear: 1964, }, releaseYear: 2003, }, { ...

Alert: An invalid value of `false` was received for the non-boolean attribute `className`. To properly write this to the DOM, please provide a string instead: className="false" or use a different

While many have tried to address this issue before, none of their solutions seem to work for me... The error I am encountering is: If you want to write it to the DOM, pass a string instead: className="false" or className={value.toString()}. If ...

Is there a way to incorporate a background image fadeIn effect for ul using CSS3?

I have a list on my webpage where each item is displayed with a CSS3 rotate animation. After the animation finishes, I want to set a background image with a FadeIn effect. To achieve this, I created a "addbg" class for the ul element like so: ul.addbg{ ...

Unable to open file after downloading via AJAX

I am facing an issue while trying to download a file using an Ajax request. Although the file is successfully downloaded, I am unable to open it. I am seeking assistance with the provided details below. Thank you. On a JSP page, there is a list ...