Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit e0d299f

Browse files
Uzlopakmohammad0-0ahmad
authored andcommittedJun 3, 2022
patch autotyping to handle properly population and
interfaces
1 parent 47469d6 commit e0d299f

File tree

9 files changed

+511
-306
lines changed

9 files changed

+511
-306
lines changed
 

‎test/types/create.test.ts

Lines changed: 170 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,187 @@
1-
import { Schema, model, Document, Types } from 'mongoose';
1+
import { Schema, model, Types, CallbackError } from 'mongoose';
2+
import { expectError, expectType } from 'tsd';
23

34
const schema: Schema = new Schema({ name: { type: 'String' } });
45

5-
interface ITest extends Document {
6+
interface ITest {
67
_id?: Types.ObjectId;
78
name?: string;
89
}
910

1011
const Test = model<ITest>('Test', schema);
1112

12-
Test.create({ _id: new Types.ObjectId('0'.repeat(24)), name: 'test' }).then((doc: ITest) => console.log(doc.name));
13+
Test.create({ _id: '000000000000000000000000', name: 'test' }).then(doc => {
14+
expectType<Types.ObjectId>(doc._id);
15+
expectType<string>(doc.name);
16+
expectType<boolean>(doc.isNew);
17+
});
1318

14-
Test.create([{ name: 'test' }], { validateBeforeSave: false }).then((docs: ITest[]) => console.log(docs[0].name));
19+
Test.create({ _id: new Types.ObjectId('000000000000000000000000'), name: 'test' }).then((doc) => {
20+
expectType<Types.ObjectId>(doc._id);
21+
expectType<string>(doc.name);
22+
expectType<boolean>(doc.isNew);
23+
});
1524

16-
Test.create({ name: 'test' }, { name: 'test2' }).then((docs: ITest[]) => console.log(docs[0].name));
25+
Test.create([{ name: 'test' }], { validateBeforeSave: false }).then(docs => {
26+
expectType<Types.ObjectId>(docs[0]._id);
27+
expectType<string>(docs[0].name);
28+
expectType<boolean>(docs[0].isNew);
29+
});
1730

18-
Test.insertMany({ name: 'test' }).then((docs: ITest[]) => console.log(docs[0].name));
31+
Test.create({ name: 'test' }, { name: 'test2' }).then(docs => {
32+
expectType<Types.ObjectId>(docs[0]._id);
33+
expectType<string>(docs[0].name);
34+
expectType<Types.ObjectId>(docs[1]._id);
35+
expectType<string>(docs[1].name);
36+
});
1937

20-
Test.create([{ name: 'test' }], { validateBeforeSave: true }).then((docs: ITest[]) => console.log(docs[0].name));
38+
Test.create([{ name: 'test' }], { validateBeforeSave: true }).then(docs => {
39+
expectType<Types.ObjectId>(docs[0]._id);
40+
expectType<string>(docs[0].name);
41+
});
2142

22-
(async() => {
43+
44+
Test.insertMany({ name: 'test' }, {}, (err, docs) => {
45+
expectType<CallbackError>(err);
46+
expectType<Types.ObjectId>(docs[0]._id);
47+
expectType<string>(docs[0].name);
48+
expectType<boolean>(docs[0].isNew);
49+
});
50+
51+
Test.insertMany({ name: 'test' }, { lean: true }, (err, docs) => {
52+
expectType<CallbackError>(err);
53+
expectType<Types.ObjectId>(docs[0]._id);
54+
expectType<string>(docs[0].name);
55+
expectError(docs[0].isNew);
56+
});
57+
58+
Test.insertMany({ name: 'test' }, (err, docs) => {
59+
expectType<CallbackError>(err);
60+
expectType<Types.ObjectId>(docs[0]._id);
61+
expectType<string>(docs[0].name);
62+
expectType<boolean>(docs[0].isNew);
63+
});
64+
65+
Test.insertMany({ name: 'test' }, {}, (err, docs) => {
66+
expectType<CallbackError>(err);
67+
expectType<Types.ObjectId>(docs[0]._id);
68+
expectType<string>(docs[0].name);
69+
expectType<boolean>(docs[0].isNew);
70+
});
71+
72+
Test.insertMany([{ name: 'test' }], { rawResult: true }, (err, result) => {
73+
expectType<CallbackError>(err);
74+
expectType<boolean>(result.acknowledged);
75+
expectType<number>(result.insertedCount);
76+
expectType<{[key: number]: Types.ObjectId;}>(result.insertedIds)
77+
});
78+
79+
Test.insertMany([{ name: 'test' }], { rawResult: true }, (err, result) => {
80+
expectType<CallbackError>(err);
81+
expectType<boolean>(result.acknowledged);
82+
expectType<number>(result.insertedCount);
83+
expectType<{[key: number]: Types.ObjectId;}>(result.insertedIds)
84+
});
85+
86+
Test.insertMany([{ name: 'test' }], { lean: true }, (err, docs) => {
87+
expectType<CallbackError>(err);
88+
expectType<Types.ObjectId>(docs[0]._id);
89+
expectType<string>(docs[0].name);
90+
expectError(docs[0].isNew);
91+
});
92+
93+
Test.insertMany([{ name: 'test' }], (err, docs) => {
94+
expectType<CallbackError>(err);
95+
expectType<Types.ObjectId>(docs[0]._id);
96+
expectType<string>(docs[0].name);
97+
expectType<boolean>(docs[0].isNew);
98+
});
99+
100+
Test.insertMany({ _id: '000000000000000000000000', name: 'test' }, (err, docs) => {
101+
expectType<Types.ObjectId>(docs[0]._id);
102+
expectType<string>(docs[0].name);
103+
expectType<boolean>(docs[0].isNew);
104+
});
105+
106+
Test.insertMany({ _id: new Types.ObjectId('000000000000000000000000')}, (err, docs) => {
107+
expectType<Types.ObjectId>(docs[0]._id);
108+
expectType<string | undefined>(docs[0].name);
109+
expectType<boolean>(docs[0].isNew);
110+
});
111+
112+
Test.insertMany({ name: 'test' }, {}).then(docs => {
113+
expectType<Types.ObjectId>(docs[0]._id);
114+
expectType<string>(docs[0].name);
115+
expectType<boolean>(docs[0].isNew);
116+
});
117+
118+
Test.insertMany({ name: 'test' }, { lean: true }).then(docs => {
119+
expectType<Types.ObjectId>(docs[0]._id);
120+
expectType<string>(docs[0].name);
121+
expectError(docs[0].isNew);
122+
});
123+
124+
Test.insertMany({ name: 'test' }).then(docs => {
125+
expectType<Types.ObjectId>(docs[0]._id);
126+
expectType<string>(docs[0].name);
127+
expectType<boolean>(docs[0].isNew);
128+
});
129+
130+
Test.insertMany({ name: 'test' }, {}).then(docs => {
131+
expectType<Types.ObjectId>(docs[0]._id);
132+
expectType<string>(docs[0].name);
133+
expectType<boolean>(docs[0].isNew);
134+
});
135+
136+
Test.insertMany([{ name: 'test' }], { rawResult: true }).then(result => {
137+
expectType<boolean>(result.acknowledged);
138+
expectType<number>(result.insertedCount);
139+
expectType<{[key: number]: Types.ObjectId;}>(result.insertedIds)
140+
});
141+
142+
Test.insertMany([{ name: 'test' }], { rawResult: true }).then(result => {
143+
expectType<boolean>(result.acknowledged);
144+
expectType<number>(result.insertedCount);
145+
expectType<{[key: number]: Types.ObjectId;}>(result.insertedIds)
146+
});
147+
148+
Test.insertMany([{ name: 'test' }], { lean: true }).then(docs => {
149+
expectType<Types.ObjectId>(docs[0]._id);
150+
expectType<string>(docs[0].name);
151+
expectError(docs[0].isNew);
152+
});
153+
154+
Test.insertMany([{ name: 'test' }], { lean: false }).then(docs => {
155+
expectType<Types.ObjectId>(docs[0]._id);
156+
expectType<string | undefined>(docs[0].name);
157+
expectType<boolean>(docs[0].isNew);
158+
});
159+
160+
Test.insertMany([{ name: 'test' }], { }).then(docs => {
161+
expectType<Types.ObjectId>(docs[0]._id);
162+
expectType<string | undefined>(docs[0].name);
163+
expectType<boolean>(docs[0].isNew);
164+
});
165+
166+
Test.insertMany([{ name: 'test' }]).then(docs => {
167+
expectType<Types.ObjectId>(docs[0]._id);
168+
expectType<string>(docs[0].name);
169+
expectType<boolean>(docs[0].isNew);
170+
});
171+
172+
Test.insertMany({ _id: '000000000000000000000000', name: 'test' }).then(docs => {
173+
expectType<Types.ObjectId>(docs[0]._id);
174+
expectType<string>(docs[0].name);
175+
expectType<boolean>(docs[0].isNew);
176+
});
177+
178+
Test.insertMany({ _id: new Types.ObjectId('000000000000000000000000'), name: 'test' }).then(docs => {
179+
expectType<Types.ObjectId>(docs[0]._id);
180+
expectType<string>(docs[0].name);
181+
expectType<boolean>(docs[0].isNew);
182+
});
183+
184+
(async () => {
23185
const [t1] = await Test.create([{ name: 'test' }]);
24186
const [t2, t3, t4] = await Test.create({ name: 'test' }, { name: 'test' }, { name: 'test' });
25187
(await Test.create([{ name: 'test' }]))[0];

‎test/types/models.test.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { Schema, Document, Model, Types, connection, model } from 'mongoose';
1+
import { ObjectId } from 'bson';
2+
import { Schema, Document, Model, connection, model, Types } from 'mongoose';
23
import { expectError, expectType } from 'tsd';
34
import { AutoTypedSchemaType, autoTypedSchema } from './schema.test';
45

@@ -41,9 +42,7 @@ function rawDocSyntax(): void {
4142

4243
const Test = connection.model<ITest, TestModel>('Test', TestSchema);
4344

44-
const bar = (SomeModel: Model<any, any, any>) => console.log(SomeModel);
45-
46-
bar(Test);
45+
expectType<Model<ITest, {}, ITestMethods, {}>>(Test);
4746

4847
const doc = new Test({ foo: '42' });
4948
console.log(doc.foo);
@@ -84,7 +83,7 @@ async function insertManyTest() {
8483
});
8584

8685
const res = await Test.insertMany([{ foo: 'bar' }], { rawResult: true });
87-
const ids: Types.ObjectId[] = Object.values(res.insertedIds);
86+
expectType<ObjectId>(res.insertedIds[0]);
8887
}
8988

9089
function schemaStaticsWithoutGenerics() {
@@ -140,13 +139,17 @@ async function gh10359() {
140139
lastName: string;
141140
}
142141

143-
async function foo<T extends Group>(model: Model<any>): Promise<T | null> {
144-
const doc: T | null = await model.findOne({ groupId: 'test' }).lean().exec();
142+
async function foo(model: Model<User, {}, {}, {}>) {
143+
const doc = await model.findOne({ groupId: 'test' }).lean().exec();
144+
expectType<string | undefined>(doc?.firstName);
145+
expectType<string | undefined>(doc?.lastName);
146+
expectType<Types.ObjectId | undefined>(doc?._id);
147+
expectType<string | undefined>(doc?.groupId);
145148
return doc;
146149
}
147150

148151
const UserModel = model<User>('gh10359', new Schema({ firstName: String, lastName: String, groupId: String }));
149-
const u: User | null = await foo<User>(UserModel);
152+
foo(UserModel);
150153
}
151154

152155
const ExpiresSchema = new Schema({

‎test/types/populate.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ const Story = model<IStory>('Story', storySchema);
7272

7373
await story.populate('author');
7474
await story.populate({ path: 'fans' });
75+
await story.populate({ path: 'fans', model: Person });
7576
await story.populate(['author']);
7677
await story.populate([{ path: 'fans' }]);
7778
await story.populate(['author', { path: 'fans' }]);

‎test/types/queries.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,10 @@ function testGenericQuery(): void {
163163

164164
function eachAsync(): void {
165165
Test.find().cursor().eachAsync((doc) => {
166-
expectType<(ITest & { _id: any; })>(doc);
166+
expectType<(ITest & { _id: Types.ObjectId; })>(doc);
167167
});
168168
Test.find().cursor().eachAsync((docs) => {
169-
expectType<(ITest & { _id: any; })[]>(docs);
169+
expectType<(ITest & { _id: Types.ObjectId; })[]>(docs);
170170
}, { batchSize: 2 });
171171
}
172172

‎test/types/utility.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { MergeType } from 'mongoose';
2+
import { expectType } from 'tsd';
3+
4+
type A = { a: string, c: number};
5+
type B = { a: number, b: string };
6+
7+
expectType<string>({} as MergeType<B, A>["a"]);
8+
expectType<string>({} as MergeType<B, A>["b"]);
9+
expectType<number>({} as MergeType<B, A>["c"]);
10+
11+
expectType<number>({} as MergeType<A, B>["a"]);
12+
expectType<string>({} as MergeType<A, B>["b"]);
13+
expectType<number>({} as MergeType<A, B>["c"]);

‎tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"compilerOptions": {
3+
"strict": true,
34
"strictNullChecks": true,
45
"paths": {
56
"mongoose" : ["./types/index.d.ts"]

‎types/index.d.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ declare module 'mongoose' {
6767
schema?: TSchema,
6868
collection?: string,
6969
options?: CompileModelOptions
70-
): Model<InferSchemaType<TSchema>, ObtainSchemaGeneric<TSchema, 'TQueryHelpers'>, ObtainSchemaGeneric<TSchema, 'TInstanceMethods'>, {}, TSchema>;
70+
): Model<InferSchemaType<TSchema>, ObtainSchemaGeneric<TSchema, 'TQueryHelpers'>, ObtainSchemaGeneric<TSchema, 'TInstanceMethods'>, {}, TSchema> & ObtainSchemaGeneric<TSchema, 'TStaticMethods'>;
7171

7272
export function model<T>(name: string, schema?: Schema<T, any, any> | Schema<T & Document, any, any>, collection?: string, options?: CompileModelOptions): Model<T>;
7373

@@ -101,7 +101,15 @@ declare module 'mongoose' {
101101
[k: string]: any
102102
}
103103

104-
export type Require_id<T> = T extends { _id?: any } ? (T & { _id: T['_id'] }) : (T & { _id: Types.ObjectId });
104+
export type Require_id<T> = T extends { _id?: infer U }
105+
? U extends any
106+
? (T & { _id: Types.ObjectId })
107+
: T & Required<{ _id: U }>
108+
: T & { _id: Types.ObjectId };
109+
110+
export type RequireOnlyTypedId<T> = T extends { _id?: infer U; }
111+
? Required<{ _id: U }>
112+
: { _id: Types.ObjectId };
105113

106114
export type HydratedDocument<DocType, TMethodsAndOverrides = {}, TVirtuals = {}> = DocType extends Document ? Require_id<DocType> : (Document<unknown, any, DocType> & Require_id<DocType> & TVirtuals & TMethodsAndOverrides);
107115

‎types/models.d.ts

Lines changed: 300 additions & 282 deletions
Large diffs are not rendered by default.

‎types/utility.d.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@ declare module 'mongoose' {
77
type UnpackedIntersection<T, U> = T extends null ? null : T extends (infer A)[]
88
? (Omit<A, keyof U> & U)[]
99
: keyof U extends never
10-
? T
11-
: Omit<T, keyof U> & U;
12-
13-
type MergeBOntoA<A, B> = Omit<A, keyof B> & B;
10+
? T
11+
: Omit<T, keyof U> & U;
1412

13+
type MergeType<A extends Record<number | string, any>, B extends Record<number | string, any>> = Omit<A, keyof B> & B;
1514

1615
}

0 commit comments

Comments
 (0)
Please sign in to comment.