4

Typescript is complaining at this line:

user.posts.pull(postId);

I am getting this error:

     Property 'pull' does not exist on type 'PostDoc[]'

since postId is received as req.params.postId it is type string, so i converted it to mongoose objectId but i still have the same error:

  user.posts.pull(mongoose.Types.ObjectId(postId));

pull() works in mongoose arrays. this line of code how I implemented in javacsript. I am converting my project to typescript. this is the user interface and schema for user model.

interface UserDoc extends mongoose.Document {
  email: string;
  password: string;
  posts: PostDoc[];
  name: string;
  status: string;
}
const userSchema = new Schema({
  email: { type: String, required: true },
  password: { type: String, required: true },
  name: { type: String, required: true },
  status: { type: String, default: "I am a new user" },
  posts: [{ type: Schema.Types.ObjectId, ref: "Post" }],
});

Here post schema and interface

interface PostDoc extends Document {
  title: string;
  content: string;
  imageUrl: string;
  creator: Types.ObjectId;
}
const postSchema = new Schema(
  {
    title: {
      type: String,
      required: true,
    },
    imageUrl: {
      type: String,
      required: true,
    },
    content: {
      type: String,
      required: true,
    },
    creator: {
      type: Schema.Types.ObjectId,
     ref: "User",
      required: true,
    },
  },
  { timestamps: true }
Yilmaz
  • 35,338
  • 10
  • 157
  • 202

1 Answers1

8

I faced a similar problem to properly type the subdocuments. I propose you the following solution in order to keep both the DTO interface and the model interface separated and strongly typed. The same would apply for your PostDoc.

UserDoc DTO

interface UserDoc {
  email: string;
  password: string;
  posts: PostDoc[];
  name: string;
  status: string;
}

UserDoc Model

export type UserDocModel = UserDoc & mongoose.Document & PostDocModel & Omit<UserDoc , 'posts'>

interface PostDocModel {
  posts: mongoose.Types.Array<PostModel>;
};

We replace the posts: PostDoc[] property with Omit for our mongoose array of PostModel, maintaining the properties synchronized. Inspiration from https://stackoverflow.com/a/36661990

In this way we could access every mongoose array method such as pull, pop, shift, etc. (https://mongoosejs.com/docs/api.html#Array)

const user = await this.userdocModel.findById(userId).exec();
user.posts.pull(postId);

Exporting the model

const User = mongoose.model<UserDocModel>('User', userSchema);
export default User;
Soumynon
  • 201
  • 1
  • 2
  • This is very helpful information. I learnt alot. one thing, how did u initialize this.userdocModel. I used like this "await UserDocModel.findById(req.userId)" but did not work – Yilmaz Nov 15 '20 at 16:52
  • I just realize that UserDocModel is just type so cannot be used instead of User – Yilmaz Nov 15 '20 at 17:04
  • @Yilmaz in my case i am using NestJS (docs.nestjs.com/techniques/mongodb) (a Node.js framework), to inject and initialize the model. However I think you should be able to export the model and use it like this: const User = mongoose.model('User', userSchema); export User; – Soumynon Nov 15 '20 at 17:20