14

I created a Mongoose Schema and added some static methods for the Model, named Campaign.

If I console.log Campaign I can see the methods present on it. The problem is I don't know where to add those methods so that Typescript is also aware of them.

If I add them to my CampaignModelInterface, they are only available for instances of the model (or at least TS thinks they are).

campaignSchema.ts

  export interface CampaignModelInterface extends CampaignInterface, Document {
      // will only show on model instance
  }

  export const CampaignSchema = new Schema({
      title: { type: String, required: true },
      titleId: { type: String, required: true }
      ...etc
  )}

  CampaignSchema.statics.getLiveCampaigns = Promise.method(function (){
      const now: Date = new Date()
      return this.find({
           $and: [{startDate: {$lte: now} }, {endDate: {$gte: now} }]
      }).exec()
  })

  const Campaign = mongoose.model<CampaignModelInterface>('Campaign', CampaignSchema)
  export default Campaign

I also tried accessing it via Campaign.schema.statics, but without luck.

Can anyone advise how to let TS know about the methods present on the Model, not the Model instances?

Daniel Nitu
  • 387
  • 3
  • 8

1 Answers1

25

I answered a very similar question over here, although I'll answer yours (mostly with the third section of my other answer) as you've provided a different schema. There is a helpful readme with the Mongoose typings which is fairly hidden away, but there's a section on static methods.


The behaviour that you described is perfectly normal - Typescript is being told that the Schema (the object which describes individual documents) has the method called getLiveCampaigns.

Instead, you need to tell Typescript that the method is on the model, not the schema. Once done, you can access static methods as per the normal Mongoose method. You can do that by the following:

// CampaignDocumentInterface should contain your schema interface,
// and should extend Document from mongoose.
export interface CampaignInterface extends CampaignDocumentInterface {
    // declare any instance methods here
}

// Model is from mongoose.Model
interface CampaignModelInterface extends Model<CampaignInterface> {
    // declare any static methods here
    getLiveCampaigns(): any; // this should be changed to the correct return type if possible.
}

export const CampaignSchema = new Schema({
    title: { type: String, required: true },
    titleId: { type: String, required: true }
    // ...etc
)}

CampaignSchema.statics.getLiveCampaigns = Promise.method(function (){
    const now: Date = new Date()
    return this.find({
        $and: [{startDate: {$lte: now} }, {endDate: {$gte: now} }]
    }).exec()
})

// Note the type on the variable, and the two type arguments (instead of one).
const Campaign: CampaignModelInterface = mongoose.model<CampaignInterface, CampaignModelInterface>('Campaign', CampaignSchema)
export default Campaign
Matt Shipton
  • 1,256
  • 1
  • 11
  • 15
  • Thank you! Surprised I didn't come across your original answer before :) Was able to get it working, as you suggested. If I got it correctly, I will now put all my Schema.static methods on CampaignModelInterface and all the Schema.method methods on CampaignDocumentInterface? – Daniel Nitu Aug 17 '17 at 09:46
  • Well I personally have it set up so that `CampaignDocumentInterface` contains just the Schema (as defined in `CampaignSchema`). `CampaignInterface` contains all of your `Schema.method` methods, and `CampaignModelInterface` contains all of your `Schema.static` methods. – Matt Shipton Aug 18 '17 at 12:32
  • You can declare your `Schema.method` methods in `CampaignDocumentInterface` too, I just prefer the separation personally. – Matt Shipton Aug 18 '17 at 12:33
  • Make sure you fallow the order: `model`. This will not work: `model` – Sunny Dec 20 '17 at 02:30
  • 2
    @Mykola the [definition](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/f40ab95d0548a7544a5515228595095dd21741c7/types/mongoose/index.d.ts#L157) of `model` in the typings file is `function model>`. Therefore the order is `model(...)`. – Matt Shipton Jan 04 '18 at 22:49