Skip to content

Commit

Permalink
docs(typescript): add section about methods to TypeScript statics page
Browse files Browse the repository at this point in the history
Fix #11682
  • Loading branch information
vkarpov15 committed May 5, 2022
1 parent 58d728e commit 97815f7
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 28 deletions.
2 changes: 1 addition & 1 deletion docs/layout.pug
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ html(lang='en')
li.pure-menu-item.tertiary-item
a.pure-menu-link(href="/docs/typescript/schemas.html", class=outputUrl === '/docs/typescript/schemas.html' ? 'selected' : '') Schemas
li.pure-menu-item.tertiary-item
a.pure-menu-link(href="/docs/typescript/statics.html", class=outputUrl === '/docs/typescript/statics.html' ? 'selected' : '') Statics
a.pure-menu-link(href="/docs/typescript/statics-and-methods.html", class=outputUrl === '/docs/typescript/statics-and-methods.html' ? 'selected' : '') Statics
li.pure-menu-item.tertiary-item
a.pure-menu-link(href="/docs/typescript/query-helpers.html", class=outputUrl === '/docs/typescript/query-helpers.html' ? 'selected' : '') Query Helpers
li.pure-menu-item.tertiary-item
Expand Down
7 changes: 5 additions & 2 deletions docs/typescript/schemas.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,20 @@ For example, the above code won't throw an error if `email` is optional in the d

## Generic parameters

The Mongoose `Schema` class in TypeScript has 3 [generic parameters](https://www.typescriptlang.org/docs/handbook/2/generics.html):
The Mongoose `Schema` class in TypeScript has 4 [generic parameters](https://www.typescriptlang.org/docs/handbook/2/generics.html):

- `DocType` - An interface descibing how the data is saved in MongoDB
- `M` - The Mongoose model type. Can be omitted if there are no query helpers or instance methods to be defined.
- default: `Model<DocType, any, any>`
- `TInstanceMethods` - An interface containing the methods for the schema.
- default: `{}`
- `TQueryHelpers` - An interface containing query helpers defined on the schema. Defaults to `{}`.

<details>
<summary>View TypeScript definition</summary>

```typescript
class Schema<DocType = any, M = Model<DocType, any, any>, TInstanceMethods = {}> extends events.EventEmitter {
class Schema<DocType = any, M = Model<DocType, any, any>, TInstanceMethods = {}, TQueryHelpers = {}> extends events.EventEmitter {
// ...
}
```
Expand All @@ -59,6 +60,8 @@ The second generic param, `M`, is the model used with the schema. Mongoose uses

The third generic param, `TInstanceMethods` is used to add types for instance methods defined in the schema.

The 4th param, `TQueryHelpers`, is used to add types for [chainable query helpers](/docs/typescript/query-helpers.html).

## Schema vs Interface fields

Mongoose checks to make sure that every path in your schema is defined in your document interface.
Expand Down
106 changes: 106 additions & 0 deletions docs/typescript/statics-and-methods.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Statics and Methods in TypeScript

You can define instance methods and static functions on Mongoose models.
With a little extra configuration, you can also register methods and statics in TypeScript.

## Methods

To define an [instance method](/docs/guide.html#methods) in TypeScript, create a new interface representing your instance methods.
You need to pass that interface as the 3rd generic parameter to the `Schema` constructor **and** as the 3rd generic parameter to `Model` as shown below.

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

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

// Put all user instance methods in this interface:
interface IUserMethods {
fullName(): string;
}

// Create a new Model type that knows about IUserMethods...
type UserModel = Model<IUser, {}, IUserMethods>;

// And a schema that knows about IUserMethods
const schema = new Schema<IUser, UserModel, IUserMethods>({
firstName: { type: String, required: true },
lastName: { type: String, required: true }
});
schema.method('fullName', function fullName() {
return this.firstName + ' ' + this.lastName;
});

const User = model<IUser, UserModel>('User', schema);

const user = new User({ firstName: 'Jean-Luc', lastName: 'Picard' });
const fullName: string = user.fullName(); // 'Jean-Luc Picard'
```

## Statics

Mongoose [models](/docs/models.html) do **not** have an explicit generic parameter for [statics](/docs/guide.html#statics).
If your model has statics, we recommend creating an interface that [extends](https://www.typescriptlang.org/docs/handbook/interfaces.html) Mongoose's `Model` interface as shown below.

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

interface IUser {
name: string;
}

interface UserModel extends Model<IUser> {
myStaticMethod(): number;
}

const schema = new Schema<IUser, UserModel>({ name: String });
schema.static('myStaticMethod', function myStaticMethod() {
return 42;
});

const User = model<IUser, UserModel>('User', schema);

const answer: number = User.myStaticMethod(); // 42
```

## Both Methods and Statics

Below is how you can define a model that has both methods and statics.

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

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

interface IUserMethods {
fullName(): string;
}

interface UserModel extends Model<IUser, {}, IUserMethods> {
createWithFullName(name: string): Promise<HydratedDocument<IUser, IUserMethods>>;
}

const schema = new Schema<IUser, UserModel, IUserMethods>({
firstName: { type: String, required: true },
lastName: { type: String, required: true }
});
schema.static('createWithFullName', function createWithFullName(name: string) {
const [firstName, lastName] = name.split(' ');
return User.create({ firstName, lastName });
});
schema.method('fullName', function fullName(): string {
return this.firstName + ' ' + this.lastName;
});

const User = model<IUser, UserModel>('User', schema);

User.createWithFullName('Jean-Luc Picard').then(doc => {
console.log(doc.firstName); // 'Jean-Luc'
doc.fullName(); // 'Jean-Luc Picard'
});
```
25 changes: 0 additions & 25 deletions docs/typescript/statics.md

This file was deleted.

0 comments on commit 97815f7

Please sign in to comment.