Automattic / mongoose

MongoDB object modeling designed to work in an asynchronous environment.
https://mongoosejs.com
MIT License
26.92k stars 3.84k forks source link

How to type added static methods and query helpers #14815

Closed Jaspper132 closed 1 month ago

Jaspper132 commented 2 months ago

Mongoose version

8.5.2

Node.js version

v20.13.1

MongoDB version

7.0

I carefully read the Typescript section in the documentation, and static methods started working for me, but the query assistants did not work. An error occurred: Property 'isAbsent' does not exist on type '{}'. After spending a lot of time, I gave up and decided to create a question. I initially created universal types for static methods and query helpers, which I connected via a global plugin. And then I passed the schema interface to the generic, which was inherited from Document. In general, the question could be called 'How to add static methods or query helpers globally, without getting into each model and not duplicating generics in interfaces', but most likely this cannot be done).

My attempt ⬇️⬇️⬇️ ./querieHelpers.type

import { QueryWithHelpers, HydratedDocument, Document } from "mongoose"

export default interface IQueryHelpers<T> {
  isAbsent(statusCode: number): Promise<QueryWithHelpers<HydratedDocument<T>[], HydratedDocument<T>, IQueryHelpers<T>>>
}

./static.type

import {
  FilterQuery,
  ProjectionType,
  QueryOptions,
  Model,
  QueryWithHelpers,
} from "mongoose";

import IQueryHelpers from "./querieHelpers.type";

export default interface IGlobalMethods<T> {
  findOneEnabled (
    this: Model<T>,
    filters: FilterQuery<T>,
    projection?: ProjectionType<T>,
    options?: QueryOptions
  ): QueryWithHelpers<
  T | null, T, IQueryHelpers<T>
>,
}

./addQuerieHelpers.plugin

import { Schema } from "mongoose";

import isAbsent  from './isAbsent.qh'

const addStatics = function(schema: Schema) {
  schema.query.isAbsent /*<===Error*/ = isAbsent
}

export default addStatics

./addStatic.plugin

import { Schema } from "mongoose";

import findOneEnabled  from './findOneEnabled.static'

const addStatics = function(schema: Schema) {
  schema.statics.findOneEnabled = findOneEnabled
}

export default addStatics

./findOneEnabled.static

import {
  FilterQuery,
  ProjectionType,
  QueryOptions,
  Model
} from "mongoose";

const findOneEnabled = async function<T> (
  this: Model<T>,
  filters: FilterQuery<T>,
  projection?: ProjectionType<T>,
  options?: QueryOptions
): Promise<T | null> {
  const updatedFilters = { ...filters, enabled: true };
  return await this.findOne(updatedFilters, projection, options);
};

export default findOneEnabled;

./isAbsent.qh

import { QueryWithHelpers, HydratedDocument} from "mongoose"

import ApiError from "../../../errors/api.error"// some error
import IQueryHelpers from "./querieHelpers.type"

const isAbsent = async function<T>(
  this: QueryWithHelpers<any, 
  HydratedDocument<T>,
  IQueryHelpers<T>>, 
  statusCode: number
): Promise<T> {
  const doc = await this.exec()
  const model = this.model.modelName
  const query = this.getQuery()

  if (!doc) {
    throw ApiError.StatusCode(statusCode, model, query)// some error
  }

  return doc
}
export default isAbsent

./model

import { Schema, Document, model, Model} from 'mongoose';

import IStaticMethods from "./static.type";
import IQueryHelpers from "./querieHelpers.type";

export interface IEntity extends Document {
  idEntity: number
}

interface ProfileModel extends Model<IEntity, IQueryHelpers<IEntity>>, IStaticMethods<IEntity> {}

const entitySchema = new Schema<
IEntity,
Model<IEntity, IQueryHelpers<IEntity>>,
{},
IQueryHelpers<IEntity>,
{},
IStaticMethods<IEntity>>({
  idEntity: {type: Number, required: true}
})

export default model<IEntity, ProfileModel>('Profile', entitySchema)

./index

import mongoose from "mongoose";

import addStatic from './addStatic.plugin'
import addQuerieHelpers from './addStatic.plugin'

mongoose.plugin(addStatic)
mongoose.plugin(addQuerieHelpers)
github-actions[bot] commented 1 month ago

This issue is stale because it has been open 14 days with no activity. Remove stale label or comment or this will be closed in 5 days