Skip to content

Instance methods on Document does not allow setting this type explicitly #15693

@I-Enderlord-I

Description

@I-Enderlord-I

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Mongoose version

8.19.1

Node.js version

20.x

MongoDB server version

5.x

Typescript version (if applicable)

5.9.3

Description

When defining a method on a Schema the this type of the function is automatically set to the hydrated document.

It makes sense (and the type definition also seems to have that intention) that this automatism only happens when the user does manually specify this.

It the case of base level documents, this can be worked around by introducing a static method that does the same thing. In the case of Subdocuments however, this is not possible, as the outer model migth not be known.

The relevant type definition at

type AddThisParameter<T, D> = {

is

  type ThisParameter<T, F> = T extends { (this: infer This): void } ? This : F;

  type AddThisParameter<T, D> = {
    [K in keyof T]: T[K] extends (...args: infer A) => infer R
      ? ThisParameter<T[K], unknown> extends unknown
        ? (this: D, ...args: A) => R
        : T[K]
      : T[K];
  };

Here the condition ThisParameter<T[K], unknown> extends unknown is always true as everything extends unknown.
This probably should be either

  type ThisParameter<T, F> = T extends { (this: infer This): void } ? This : F;

  type AddThisParameter<T, D> = {
    [K in keyof T]: T[K] extends (...args: infer A) => infer R
      ? ThisParameter<T[K], never> extends never
        ? (this: D, ...args: A) => R
        : T[K]
      : T[K];
  };

or

  type AddThisParameter<T, D> = {
    [K in keyof T]: T[K] extends (this: infer This, ...args: infer A) => infer R
      ? (this: D, ...args: A) => R
    : T[K];
  };

as everything extends unknown.

Steps to Reproduce

Consider

interface IUser {
	name: string;
}

interface UserMethods {
	printName(this: IUser): void;
}

const schema = new Schema<IUser, Model<IUser>, UserMethods>({ name: { type: String, required: true } });
schema.method('printName', function printName() {
	console.log(this.name);
});
const User = model('user', schema);

It this case I want to use printName on a lean document at some point like

let leanInst = await User.findOne({}).lean();
User.schema.methods.printName.apply(leanInst);

This currently throws as the this type of printName is the hydrated user document.

Expected Behavior

The printName method to have the specified this type IUser, so that no TS error occurs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions