Skip to content

Instructions for typescript #74

Closed
Automattic/mongoose
#15391
@richardsimko

Description

@richardsimko

The instructions in this repo seem to contradict the main mongoose docs. According to the main docs (https://mongoosejs.com/docs/typescript/virtuals.html#set-virtuals-type-manually) virtuals should be defined after creating the schema:

interface UserDoc {
  firstName: string;
  lastName: string;
}

interface UserVirtuals {
  fullName: string;
}

type UserModel = Model<UserDoc, {}, UserVirtuals>; // <-- add virtuals here...

const schema = new Schema<UserDoc, UserModel, UserVirtuals>({
  firstName: String,
  lastName: String,
});

schema.virtual('fullName', {
  get() {
    return `${this.firstName} ${this.lastName}`;
  },
});

const UserModel = model('User', schema);

const user = await UserModel.findOne().lean<UserDoc & VirtualsForModel<typeof UserModel>>({ virtuals: true }).orFail();

console.log(user.fullName); // <--- Property 'fullName' does not exist on type 'UserDoc'.ts(2339)

This works well in the sense that there are no errors from defining the virtuals, however the inferred type in VirtualsForModel aren't correct.

According to the docs in this repo virtuals should instead be defined when creating the schema (https://github.com/mongoosejs/mongoose-lean-virtuals?tab=readme-ov-file#typescript):

const testSchema = new Schema(
  {
    name: { type: String, required: true },
  },
  {
    virtuals: {
      nameUpper: {
        get() {
          return this.name.toUpperCase();
        },
      },
    },
  },
);

However if one needs to define the model interface in the schema (Which is standard practice, just as in the above mongoose docs), this approach no longer works:

interface ITest {
  name: string;
}

interface TestVirtuals {
  nameUpper: string;
}
type TestModel = Model<ITest, {}, TestVirtuals>;

const testSchema = new Schema<ITest, TestModel, TestVirtuals>(
  {
    name: { type: String, required: true },
  },
  {
    virtuals: {
      nameUpper: {
        get() { // <--- 'get' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.ts(7023)
          return this.name.toUpperCase(); // <--- Property 'name' does not exist on type '{ get(): any; }'.ts(2339)
        },
      },
    },
  },
);

Is there any way to define lean virtuals which is also compatible with defining the interface when creating the schema?

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