Skip to content

Commit c054618

Browse files
authored
Merge pull request #15421 from Automattic/vkarpov15/docs-fixes
docs: emphasize automatic type inference in TypeScript intro and statics/methods, remove duplicated statics.md
2 parents fac1a4b + abe2c6c commit c054618

File tree

4 files changed

+86
-180
lines changed

4 files changed

+86
-180
lines changed

docs/typescript.md

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,46 @@ This guide describes Mongoose's recommended approach to working with Mongoose in
88

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

11-
1. Create an interface representing a document in MongoDB.
12-
2. Create a [Schema](guide.html) corresponding to the document interface.
13-
3. Create a Model.
14-
4. [Connect to MongoDB](connections.html).
11+
1. Create a [Schema](guide.html).
12+
2. Create a Model.
13+
3. [Connect to MongoDB](connections.html).
14+
15+
```typescript
16+
import { Schema, model, connect } from 'mongoose';
17+
18+
// 1. Create a Schema corresponding to the document interface.
19+
const userSchema = new Schema({
20+
name: { type: String, required: true },
21+
email: { type: String, required: true },
22+
avatar: String
23+
});
24+
25+
// 2. Create a Model.
26+
const User = model('User', userSchema);
27+
28+
run().catch(err => console.log(err));
29+
30+
async function run() {
31+
// 3. Connect to MongoDB
32+
await connect('mongodb://127.0.0.1:27017/test');
33+
34+
const user = new User({
35+
name: 'Bill',
36+
37+
avatar: 'https://i.imgur.com/dM7Thhn.png'
38+
});
39+
await user.save();
40+
41+
const email: string = user.email;
42+
console.log(email); // '[email protected]'
43+
}
44+
```
45+
46+
## Using Generics
47+
48+
By default, Mongoose automatically infers the shape of your documents based on your schema definition.
49+
However, if you modify your schema after your `new Schema()` call (like with plugins) then Mongoose's inferred type may be incorrect.
50+
For cases where Mongoose's automatic schema type inference is incorrect, you can define a raw document interface that tells Mongoose the type of documents in your database as follows.
1551

1652
```typescript
1753
import { Schema, model, connect } from 'mongoose';
@@ -67,8 +103,6 @@ const user: HydratedDocument<IUser> = new User({
67103
});
68104
```
69105

70-
## ObjectIds and Other Mongoose Types
71-
72106
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.
73107

74108
```ts
@@ -106,4 +140,4 @@ However, before you do, please [open an issue on Mongoose's GitHub page](https:/
106140

107141
## Next Up
108142

109-
Now that you've seen the basics of how to use Mongoose in TypeScript, let's take a look at [statics in TypeScript](typescript/statics-and-methods.html).
143+
Now that you've seen the basics of how to use Mongoose in TypeScript, let's take a look at [methods in TypeScript](typescript/statics-and-methods.html).
Lines changed: 43 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,37 @@
1-
# Statics and Methods in TypeScript
1+
# Statics in TypeScript
22

3-
You can define instance methods and static functions on Mongoose models.
4-
With a little extra configuration, you can also register methods and statics in TypeScript.
5-
6-
## Methods
7-
8-
To define an [instance method](../guide.html#methods) in TypeScript, create a new interface representing your instance methods.
9-
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.
3+
To use Mongoose's automatic type inference to define types for your [statics](../guide.html#statics) and [methods](../guide.html#methods), you should define your methods and statics using the `methods` and `statics` schema options as follows.
4+
Do **not** use the `Schema.prototype.method()` and `Schema.prototype.static()` functions, because Mongoose's automatic type inference system cannot detect methods and statics defined using those functions.
105

116
```typescript
12-
import { Model, Schema, model } from 'mongoose';
13-
14-
interface IUser {
15-
firstName: string;
16-
lastName: string;
17-
}
18-
19-
// Put all user instance methods in this interface:
20-
interface IUserMethods {
21-
fullName(): string;
22-
}
23-
24-
// Create a new Model type that knows about IUserMethods...
25-
type UserModel = Model<IUser, {}, IUserMethods>;
26-
27-
// And a schema that knows about IUserMethods
28-
const schema = new Schema<IUser, UserModel, IUserMethods>({
29-
firstName: { type: String, required: true },
30-
lastName: { type: String, required: true }
31-
});
32-
schema.method('fullName', function fullName() {
33-
return this.firstName + ' ' + this.lastName;
34-
});
35-
36-
const User = model<IUser, UserModel>('User', schema);
7+
const userSchema = new mongoose.Schema(
8+
{ name: { type: String, required: true } },
9+
{
10+
methods: {
11+
updateName(name: string) {
12+
this.name = name;
13+
return this.save();
14+
}
15+
},
16+
statics: {
17+
createWithName(name: string) {
18+
return this.create({ name });
19+
}
20+
}
21+
}
22+
);
23+
const UserModel = mongoose.model('User', userSchema);
3724

38-
const user = new User({ firstName: 'Jean-Luc', lastName: 'Picard' });
39-
const fullName: string = user.fullName(); // 'Jean-Luc Picard'
25+
const doc = new UserModel({ name: 'test' });
26+
// Compiles correctly
27+
doc.updateName('foo');
28+
// Compiles correctly
29+
UserModel.createWithName('bar');
4030
```
4131

42-
## Statics
32+
## With Generics
4333

34+
We recommend using Mongoose's automatic type inference where possible, but you can use `Schema` and `Model` generics to set up type inference for your statics and methods.
4435
Mongoose [models](../models.html) do **not** have an explicit generic parameter for [statics](../guide.html#statics).
4536
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.
4637

@@ -51,78 +42,41 @@ interface IUser {
5142
name: string;
5243
}
5344

54-
interface UserModel extends Model<IUser> {
45+
interface UserModelType extends Model<IUser> {
5546
myStaticMethod(): number;
5647
}
5748

58-
const schema = new Schema<IUser, UserModel>({ name: String });
49+
const schema = new Schema<IUser, UserModelType>({ name: String });
5950
schema.static('myStaticMethod', function myStaticMethod() {
6051
return 42;
6152
});
6253

63-
const User = model<IUser, UserModel>('User', schema);
54+
const User = model<IUser, UserModelType>('User', schema);
6455

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

68-
Mongoose does support auto typed static functions now that it is supplied in schema options.
69-
Statics functions can be defined as following:
70-
71-
```typescript
72-
import { Schema, model } from 'mongoose';
73-
74-
const schema = new Schema(
75-
{ name: String },
76-
{
77-
statics: {
78-
myStaticMethod() {
79-
return 42;
80-
}
81-
}
82-
}
83-
);
84-
85-
const User = model('User', schema);
86-
87-
const answer = User.myStaticMethod(); // 42
88-
```
89-
90-
## Both Methods and Statics
91-
92-
Below is how you can define a model that has both methods and statics.
59+
You should pass methods as the 3rd generic param to the `Schema` constructor as follows.
9360

9461
```typescript
95-
import { Model, Schema, HydratedDocument, model } from 'mongoose';
62+
import { Model, Schema, model } from 'mongoose';
9663

9764
interface IUser {
98-
firstName: string;
99-
lastName: string;
100-
}
101-
102-
interface IUserMethods {
103-
fullName(): string;
65+
name: string;
10466
}
10567

106-
interface UserModel extends Model<IUser, {}, IUserMethods> {
107-
createWithFullName(name: string): Promise<HydratedDocument<IUser, IUserMethods>>;
68+
interface UserMethods {
69+
updateName(name: string): Promise<any>;
10870
}
10971

110-
const schema = new Schema<IUser, UserModel, IUserMethods>({
111-
firstName: { type: String, required: true },
112-
lastName: { type: String, required: true }
72+
const schema = new Schema<IUser, Model<IUser>, UserMethods>({ name: String });
73+
schema.method('updateName', function updateName(name) {
74+
this.name = name;
75+
return this.save();
11376
});
114-
schema.static('createWithFullName', function createWithFullName(name: string) {
115-
const [firstName, lastName] = name.split(' ');
116-
return this.create({ firstName, lastName });
117-
});
118-
schema.method('fullName', function fullName(): string {
119-
return this.firstName + ' ' + this.lastName;
120-
});
121-
122-
const User = model<IUser, UserModel>('User', schema);
12377

124-
User.createWithFullName('Jean-Luc Picard').then(doc => {
125-
console.log(doc.firstName); // 'Jean-Luc'
126-
doc.fullName(); // 'Jean-Luc Picard'
127-
});
78+
const User = model('User', schema);
79+
const doc = new User({ name: 'test' });
80+
// Compiles correctly
81+
doc.updateName('foo');
12882
```

docs/typescript/statics.md

Lines changed: 0 additions & 82 deletions
This file was deleted.

docs/typescript/virtuals.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@ interface UserVirtuals {
7272
fullName: string;
7373
}
7474

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

77-
const schema = new Schema<UserDoc, UserModel, {}, {}, UserVirtuals>({ // <-- and here
77+
const schema = new Schema<UserDoc, UserModelType, {}, {}, UserVirtuals>({ // <-- and here
7878
firstName: String,
7979
lastName: String
8080
});

0 commit comments

Comments
 (0)