'Mongoose & TypeScript - Property '_doc' does not exist on type 'IEventModel'

I'm learning some JavaScript backend programming from a course I'm taking. It focuses on ExpressJS, MongoDB, and GraphQL. Because I like making things more challenging for myself, I decided to also brush up on my TypeScript while I'm at it by doing all the coursework in TypeScript.

Anyway, so I'm using verison 5.5.6 of mongoose and @types/mongoose. Here is my interface for the type of the DB record:

export default interface IEvent {
    _id: any;
    title: string;
    description: string;
    price: number;
    date: string | Date;
}

Then I create the Mongoose Model like this:

import { Document, Schema, model } from 'mongoose';
import IEvent from '../ts-types/Event.type';

export interface IEventModel extends IEvent, Document {}

const eventSchema: Schema = new Schema({
    title: {
        type: String,
        required: true
    },
    description: {
        type: String,
        required: true
    },
    price: {
        type: Number,
        required: true
    },
    date: {
        type: Date,
        required: true
    }
});

export default model<IEventModel>('Event', eventSchema);

Lastly, I have written the following resolver for a GraphQL mutation:

createEvent: async (args: ICreateEventArgs): Promise<IEvent> => {
            const { eventInput } = args;
            const event = new EventModel({
                title: eventInput.title,
                description: eventInput.description,
                price: +eventInput.price,
                date: new Date(eventInput.date)
            });
            try {
                const result: IEventModel = await event.save();
                return { ...result._doc };
            } catch (ex) {
                console.log(ex); // tslint:disable-line no-console
                throw ex;
            }
        }

My problem is that TypeScript gives me an error that "._doc" is not a property on "result". The exact error is:

error TS2339: Property '_doc' does not exist on type 'IEventModel'.

I can't figure out what I'm doing wrong. I've reviewed the documentation many times and it seems that I should have all the correct Mongoose properties here. For the time being I'm going to add the property to my own interface just to move on with the course, but I'd prefer help with identifying the correct solution here.



Solution 1:[1]

interface MongoResult {
  _doc: any
}

export default interface IEvent extends MongoResult {
    _id: any;
    title: string;
    description: string;
    price: number;
    date: string | Date;
}

Then you still have to deal with casting the _doc back to your own IEvent...

Solution 2:[2]

add _doc with type any to your custom Model interface

interface IUser extends Document {
 ...
 _doc: any
}

a full example is here https://github.com/apotox/express-mongoose-typescript-starter

Solution 3:[3]

This might be a late answer but serves for all that come searching this.

inteface DocumentResult<T> {
    _doc: T;
}

interface IEvent extends DocumentResult<IEvent> {
    _id: any;
    title: string;
    description: string;
    price: number;
    date: string | Date;
}

Now when you call for (...)._doc , _doc will be of type _doc and vscode will be able to interpert your type. Just with a generic declaration. Also instead of creating an interface for holding that property you could include it inside IEvent with the type of IEvent.

Solution 4:[4]

This is something that I do when I always use typescript alongside mongoose, first things first we should define interfaces for the schema and model:

export interface IDummy {
  something: string;
  somethingElse: string;
}

export interface DummyDocument extends IDummy, mongoose.Document {
  createdAt: Date;
  updatedAt: Date;
  _doc?: any
}

second we should create out schema:

const DummySchema = new mongoose.Schema<DummyDocument>({
  something: String,
  somethingElse: String,
})

finally we are going to use export model pattern for exporting our model as a module from file:

export const DummyModel = mongoose.model<DummyDocument>

Now the problem has fixed and you are not going to see the typescript error, we have manually attached the _doc to our model with generics that the aid of generics.

Solution 5:[5]

The _doc field will be a circular reference. So an easy way to go about it is to simply do something like this. It also avoids infinite circular references by omitting itself in the child record. No interface extension is required!

export default interface IEvent {
    _doc: Omit<this,'_doc'>;
}

Solution 6:[6]

For some reasons, the structure of the return type is not included in @types/mongoose lib. So each time you want to de-structure the return object you get an error that the variable in not definition in the interface signature of both document and your custom types. That should be some sort of bug i guess.

The solution is to return the result itself, that will automatically return the data defined in interface (IEvent) without the meta data .

...    
try {
    const result = await event.save();
                return result;
            } catch (ex) {
                throw ex;
            }
...

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Holt Mansfield
Solution 2
Solution 3 RageousFoxTrot
Solution 4 Emad Baqeri
Solution 5 obax
Solution 6 handsben