Skip to content

Latest commit

 

History

History
101 lines (76 loc) · 2.81 KB

virtuals.md

File metadata and controls

101 lines (76 loc) · 2.81 KB

Virtuals in TypeScript

Virtuals are computed properties: you can access virtuals on hydrated Mongoose documents, but virtuals are not stored in MongoDB. Mongoose supports auto typed virtuals so you don't need to define additional typescript interface anymore but you are still able to do so.

Automatically Inferred Types

To make mongoose able to infer virtuals type, You have to define them in schema constructor as following:

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

const schema = new Schema(
  {
    firstName: String,
    lastName: String
  },
  {
    virtuals: {
      fullName: {
        get() {
          return `${this.firstName} ${this.lastName}`;
        }
        // virtual setter and options can be defined here as well.
      }
    }
  }
);

Note that Mongoose does not include virtuals in the returned type from InferSchemaType. That is because InferSchemaType returns a value similar to the raw document interface, which represents the structure of the data stored in MongoDB.

type User = InferSchemaType<typeof schema>;

const user: User = {};
// Property 'fullName' does not exist on type '{ firstName?: string | undefined; ... }'.
user.fullName;

However, Mongoose does add the virtuals to the model type.

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

const user = new UserModel({ firstName: 'foo' });
// Works
user.fullName;

// Here's how to get the hydrated document type
type UserDocument = ReturnType<(typeof UserModel)['hydrate']>;

Set virtuals type manually

You shouldn't define virtuals in your TypeScript document interface. Instead, you should define a separate interface for your virtuals, and pass this interface to Model and Schema.

For example, suppose you have a UserDoc interface, and you want to define a fullName virtual. Below is how you can define a separate UserVirtuals interface for fullName.

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

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>({ // <-- and here
  firstName: String,
  lastName: String
});

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

Override the Type of this in Your Virtual

In case the value of this in your virtual is incorrect for some reason, you can always override it using the generic parameter in the virtual() function.

interface MyCustomUserDocumentType {
  firstName: string;
  lastName: string;
  myMethod(): string;
}

schema.virtual<MyCustomUserDocumentType>('fullName').get(function() {
  return this.method(); // returns string
});