Revision 3edadd7196e1850835e341255e4f835a6e951ab3 authored by Valeri Karpov on 21 February 2022, 19:09:42 UTC, committed by Valeri Karpov on 21 February 2022, 19:09:42 UTC
1 parent 85361a4
Raw File
typescript.md
# TypeScript Support

Mongoose introduced [officially supported TypeScript bindings in v5.11.0](https://thecodebarbarian.com/working-with-mongoose-in-typescript.html).
Mongoose's `index.d.ts` file supports a wide variety of syntaxes and strives to be compatible with `@types/mongoose` where possible.
This guide describes Mongoose's recommended approach to working with Mongoose in TypeScript.

### Creating Your First Document

To get started with Mongoose in TypeScript, you need to: 

1. Create an interface representing a document in MongoDB.
2. Create a [Schema](/docs/guide.html) corresponding to the document interface.
3. Create a Model.
4. [Connect to MongoDB](/docs/connections.html).

```typescript
import { Schema, model, connect } from 'mongoose';

// 1. Create an interface representing a document in MongoDB.
interface User {
  name: string;
  email: string;
  avatar?: string;
}

// 2. Create a Schema corresponding to the document interface.
const schema = new Schema<User>({
  name: { type: String, required: true },
  email: { type: String, required: true },
  avatar: String
});

// 3. Create a Model.
const UserModel = model<User>('User', schema);

run().catch(err => console.log(err));

async function run(): Promise<void> {
  // 4. Connect to MongoDB
  await connect('mongodb://localhost:27017/test');

  const doc = new UserModel({
    name: 'Bill',
    email: 'bill@initech.com',
    avatar: 'https://i.imgur.com/dM7Thhn.png'
  });
  await doc.save();

  console.log(doc.email); // 'bill@initech.com'
}
```

You as the developer are responsible for ensuring that your document interface lines up with your Mongoose schema.
For example, Mongoose won't report an error if `email` is `required` in your Mongoose schema but optional in your document interface.

The `UserModel()` constructor returns an instance of `HydratedDocument<User>`.
`User` is a _document interface_, it represents the raw object structure that `User` objects look like in MongoDB.
`HydratedDocument<User>` represents a hydrated Mongoose document, with methods, virtuals, and other Mongoose-specific features.

```ts
import { HydratedDocument } from 'mongoose';

const doc: HydratedDocument<User> = new UserModel({
  name: 'Bill',
  email: 'bill@initech.com',
  avatar: 'https://i.imgur.com/dM7Thhn.png'
});
```

### ObjectIds and Other Mongoose Types

To define a property of type `ObjectId`, you should use `Types.ObjectId` in the TypeScript document interface. You should use `'ObjectId'` or `Schema.Types.ObjectId` in your schema definition.

```ts
import { Schema, Types } from 'mongoose';

// 1. Create an interface representing a document in MongoDB.
interface User {
  name: string;
  email: string;
  // Use `Types.ObjectId` in document interface...
  organization: Types.ObjectId;
}

// 2. Create a Schema corresponding to the document interface.
const schema = new Schema<User>({
  name: { type: String, required: true },
  email: { type: String, required: true },
  // And `Schema.Types.ObjectId` in the schema definition.
  organization: { type: Schema.Types.ObjectId, ref: 'Organization' }
});
```

That's because `Schema.Types.ObjectId` is a [class that inherits from SchemaType](/docs/schematypes.html), **not** the class you use to create a new MongoDB ObjectId.

### Using `extends Document`

Alternatively, your document interface can extend Mongoose's `Document` class.
Many Mongoose TypeScript codebases use the below approach.

```typescript
import { Document, Schema, model, connect } from 'mongoose';

interface User extends Document {
  name: string;
  email: string;
  avatar?: string;
}
```

This approach works, but we recommend your document interface _not_ extend `Document`.
Using `extends Document` makes it difficult for Mongoose to infer which properties are present on [query filters](/docs/queries.html), [lean documents](/docs/tutorials/lean.html), and other cases.

We recommend your document interface contain the properties defined in your schema and line up with what your documents look like in MongoDB.
Although you can add [instance methods](/docs/guide.html#methods) to your document interface, we do not recommend doing so.

### Using Custom Bindings

If Mongoose's built-in `index.d.ts` file does not work for you, you can remove it in a postinstall script in your `package.json` as shown below.
However, before you do, please [open an issue on Mongoose's GitHub page](https://github.com/Automattic/mongoose/issues/new) and describe the issue you're experiencing.

```
{
  "postinstall": "rm ./node_modules/mongoose/index.d.ts"
}
```

### Next Up

Now that you've seen the basics of how to use Mongoose in TypeScript, let's take a look at [statics in TypeScript](/docs/typescript/statics.html).
back to top