Skip to content

Commit

Permalink
Merge branch 'Uzlopak-patch-autotypes' into auto-typed-schema
Browse files Browse the repository at this point in the history
  • Loading branch information
mohammad0-0ahmad committed Jun 3, 2022
2 parents e4d5512 + 7929b25 commit 6810555
Show file tree
Hide file tree
Showing 10 changed files with 483 additions and 274 deletions.
178 changes: 170 additions & 8 deletions test/types/create.test.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,187 @@
import { Schema, model, Document, Types } from 'mongoose';
import { Schema, model, Types, CallbackError } from 'mongoose';
import { expectError, expectType } from 'tsd';

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

interface ITest extends Document {
interface ITest {
_id?: Types.ObjectId;
name?: string;
}

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

Test.create({ _id: new Types.ObjectId('0'.repeat(24)), name: 'test' }).then((doc: ITest) => console.log(doc.name));
Test.create({ _id: '000000000000000000000000', name: 'test' }).then(doc => {
expectType<Types.ObjectId>(doc._id);
expectType<string>(doc.name);
expectType<boolean>(doc.isNew);
});

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

Test.create({ name: 'test' }, { name: 'test2' }).then((docs: ITest[]) => console.log(docs[0].name));
Test.create([{ name: 'test' }], { validateBeforeSave: false }).then(docs => {
expectType<Types.ObjectId>(docs[0]._id);
expectType<string>(docs[0].name);
expectType<boolean>(docs[0].isNew);
});

Test.insertMany({ name: 'test' }).then((docs: ITest[]) => console.log(docs[0].name));
Test.create({ name: 'test' }, { name: 'test2' }).then(docs => {
expectType<Types.ObjectId>(docs[0]._id);
expectType<string>(docs[0].name);
expectType<Types.ObjectId>(docs[1]._id);
expectType<string>(docs[1].name);
});

Test.create([{ name: 'test' }], { validateBeforeSave: true }).then((docs: ITest[]) => console.log(docs[0].name));
Test.create([{ name: 'test' }], { validateBeforeSave: true }).then(docs => {
expectType<Types.ObjectId>(docs[0]._id);
expectType<string>(docs[0].name);
});

(async() => {

Test.insertMany({ name: 'test' }, {}, (err, docs) => {
expectType<CallbackError>(err);
expectType<Types.ObjectId>(docs[0]._id);
expectType<string>(docs[0].name);
expectType<boolean>(docs[0].isNew);
});

Test.insertMany({ name: 'test' }, { lean: true }, (err, docs) => {
expectType<CallbackError>(err);
expectType<Types.ObjectId>(docs[0]._id);
expectType<string>(docs[0].name);
expectError(docs[0].isNew);
});

Test.insertMany({ name: 'test' }, (err, docs) => {
expectType<CallbackError>(err);
expectType<Types.ObjectId>(docs[0]._id);
expectType<string>(docs[0].name);
expectType<boolean>(docs[0].isNew);
});

Test.insertMany({ name: 'test' }, {}, (err, docs) => {
expectType<CallbackError>(err);
expectType<Types.ObjectId>(docs[0]._id);
expectType<string>(docs[0].name);
expectType<boolean>(docs[0].isNew);
});

Test.insertMany([{ name: 'test' }], { rawResult: true }, (err, result) => {
expectType<CallbackError>(err);
expectType<boolean>(result.acknowledged);
expectType<number>(result.insertedCount);
expectType<{[key: number]: Types.ObjectId;}>(result.insertedIds)
});

Test.insertMany([{ name: 'test' }], { rawResult: true }, (err, result) => {
expectType<CallbackError>(err);
expectType<boolean>(result.acknowledged);
expectType<number>(result.insertedCount);
expectType<{[key: number]: Types.ObjectId;}>(result.insertedIds)
});

Test.insertMany([{ name: 'test' }], { lean: true }, (err, docs) => {
expectType<CallbackError>(err);
expectType<Types.ObjectId>(docs[0]._id);
expectType<string>(docs[0].name);
expectError(docs[0].isNew);
});

Test.insertMany([{ name: 'test' }], (err, docs) => {
expectType<CallbackError>(err);
expectType<Types.ObjectId>(docs[0]._id);
expectType<string>(docs[0].name);
expectType<boolean>(docs[0].isNew);
});

Test.insertMany({ _id: '000000000000000000000000', name: 'test' }, (err, docs) => {
expectType<Types.ObjectId>(docs[0]._id);
expectType<string>(docs[0].name);
expectType<boolean>(docs[0].isNew);
});

Test.insertMany({ _id: new Types.ObjectId('000000000000000000000000')}, (err, docs) => {
expectType<Types.ObjectId>(docs[0]._id);
expectType<string | undefined>(docs[0].name);
expectType<boolean>(docs[0].isNew);
});

Test.insertMany({ name: 'test' }, {}).then(docs => {
expectType<Types.ObjectId>(docs[0]._id);
expectType<string>(docs[0].name);
expectType<boolean>(docs[0].isNew);
});

Test.insertMany({ name: 'test' }, { lean: true }).then(docs => {
expectType<Types.ObjectId>(docs[0]._id);
expectType<string>(docs[0].name);
expectError(docs[0].isNew);
});

Test.insertMany({ name: 'test' }).then(docs => {
expectType<Types.ObjectId>(docs[0]._id);
expectType<string>(docs[0].name);
expectType<boolean>(docs[0].isNew);
});

Test.insertMany({ name: 'test' }, {}).then(docs => {
expectType<Types.ObjectId>(docs[0]._id);
expectType<string>(docs[0].name);
expectType<boolean>(docs[0].isNew);
});

Test.insertMany([{ name: 'test' }], { rawResult: true }).then(result => {
expectType<boolean>(result.acknowledged);
expectType<number>(result.insertedCount);
expectType<{[key: number]: Types.ObjectId;}>(result.insertedIds)
});

Test.insertMany([{ name: 'test' }], { rawResult: true }).then(result => {
expectType<boolean>(result.acknowledged);
expectType<number>(result.insertedCount);
expectType<{[key: number]: Types.ObjectId;}>(result.insertedIds)
});

Test.insertMany([{ name: 'test' }], { lean: true }).then(docs => {
expectType<Types.ObjectId>(docs[0]._id);
expectType<string>(docs[0].name);
expectError(docs[0].isNew);
});

Test.insertMany([{ name: 'test' }], { lean: false }).then(docs => {
expectType<Types.ObjectId>(docs[0]._id);
expectType<string | undefined>(docs[0].name);
expectType<boolean>(docs[0].isNew);
});

Test.insertMany([{ name: 'test' }], { }).then(docs => {
expectType<Types.ObjectId>(docs[0]._id);
expectType<string | undefined>(docs[0].name);
expectType<boolean>(docs[0].isNew);
});

Test.insertMany([{ name: 'test' }]).then(docs => {
expectType<Types.ObjectId>(docs[0]._id);
expectType<string>(docs[0].name);
expectType<boolean>(docs[0].isNew);
});

Test.insertMany({ _id: '000000000000000000000000', name: 'test' }).then(docs => {
expectType<Types.ObjectId>(docs[0]._id);
expectType<string>(docs[0].name);
expectType<boolean>(docs[0].isNew);
});

Test.insertMany({ _id: new Types.ObjectId('000000000000000000000000'), name: 'test' }).then(docs => {
expectType<Types.ObjectId>(docs[0]._id);
expectType<string>(docs[0].name);
expectType<boolean>(docs[0].isNew);
});

(async () => {
const [t1] = await Test.create([{ name: 'test' }]);
const [t2, t3, t4] = await Test.create({ name: 'test' }, { name: 'test' }, { name: 'test' });
(await Test.create([{ name: 'test' }]))[0];
Expand Down
19 changes: 11 additions & 8 deletions test/types/models.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Schema, Document, Model, Types, connection, model } from 'mongoose';
import { ObjectId } from 'bson';
import { Schema, Document, Model, connection, model, Types } from 'mongoose';
import { expectError, expectType } from 'tsd';
import { AutoTypedSchemaType, autoTypedSchema } from './schema.test';

Expand Down Expand Up @@ -41,9 +42,7 @@ function rawDocSyntax(): void {

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

const bar = (SomeModel: Model<any, any, any>) => console.log(SomeModel);

bar(Test);
expectType<Model<ITest, {}, ITestMethods, {}>>(Test);

const doc = new Test({ foo: '42' });
console.log(doc.foo);
Expand Down Expand Up @@ -84,7 +83,7 @@ async function insertManyTest() {
});

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

function schemaStaticsWithoutGenerics() {
Expand Down Expand Up @@ -140,13 +139,17 @@ async function gh10359() {
lastName: string;
}

async function foo<T extends Group>(model: Model<any>): Promise<T | null> {
const doc: T | null = await model.findOne({ groupId: 'test' }).lean().exec();
async function foo(model: Model<User, {}, {}, {}>) {
const doc = await model.findOne({ groupId: 'test' }).lean().exec();
expectType<string | undefined>(doc?.firstName);
expectType<string | undefined>(doc?.lastName);
expectType<Types.ObjectId | undefined>(doc?._id);
expectType<string | undefined>(doc?.groupId);
return doc;
}

const UserModel = model<User>('gh10359', new Schema({ firstName: String, lastName: String, groupId: String }));
const u: User | null = await foo<User>(UserModel);
foo(UserModel);
}

const ExpiresSchema = new Schema({
Expand Down
1 change: 1 addition & 0 deletions test/types/populate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ const Story = model<IStory>('Story', storySchema);

await story.populate('author');
await story.populate({ path: 'fans' });
await story.populate({ path: 'fans', model: Person });
await story.populate(['author']);
await story.populate([{ path: 'fans' }]);
await story.populate(['author', { path: 'fans' }]);
Expand Down
4 changes: 2 additions & 2 deletions test/types/queries.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,10 @@ function testGenericQuery(): void {

function eachAsync(): void {
Test.find().cursor().eachAsync((doc) => {
expectType<(ITest & { _id: any; })>(doc);
expectType<(ITest & { _id: Types.ObjectId; })>(doc);
});
Test.find().cursor().eachAsync((docs) => {
expectType<(ITest & { _id: any; })[]>(docs);
expectType<(ITest & { _id: Types.ObjectId; })[]>(docs);
}, { batchSize: 2 });
}

Expand Down
13 changes: 13 additions & 0 deletions test/types/utility.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { MergeType } from 'mongoose';
import { expectType } from 'tsd';

type A = { a: string, c: number};
type B = { a: number, b: string };

expectType<string>({} as MergeType<B, A>["a"]);
expectType<string>({} as MergeType<B, A>["b"]);
expectType<number>({} as MergeType<B, A>["c"]);

expectType<number>({} as MergeType<A, B>["a"]);
expectType<string>({} as MergeType<A, B>["b"]);
expectType<number>({} as MergeType<A, B>["c"]);
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"compilerOptions": {
"strict": true,
"strictNullChecks": true,
"paths": {
"mongoose" : ["./types/index.d.ts"]
Expand Down
12 changes: 10 additions & 2 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ declare module 'mongoose' {
schema?: TSchema,
collection?: string,
options?: CompileModelOptions
): Model<InferSchemaType<TSchema>, ObtainSchemaGeneric<TSchema, 'TQueryHelpers'>, ObtainSchemaGeneric<TSchema, 'TInstanceMethods'>, {}, TSchema>;
): Model<InferSchemaType<TSchema>, ObtainSchemaGeneric<TSchema, 'TQueryHelpers'>, ObtainSchemaGeneric<TSchema, 'TInstanceMethods'>, {}, TSchema> & ObtainSchemaGeneric<TSchema, 'TStaticMethods'>;

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

Expand Down Expand Up @@ -101,7 +101,15 @@ declare module 'mongoose' {
[k: string]: any
}

export type Require_id<T> = T extends { _id?: any } ? (T & { _id: T['_id'] }) : (T & { _id: Types.ObjectId });
export type Require_id<T> = T extends { _id?: infer U }
? U extends any
? (T & { _id: Types.ObjectId })
: T & Required<{ _id: U }>
: T & { _id: Types.ObjectId };

export type RequireOnlyTypedId<T> = T extends { _id?: infer U; }
? Required<{ _id: U }>
: { _id: Types.ObjectId };

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

Expand Down
2 changes: 1 addition & 1 deletion types/inferschematype.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,4 @@ type ResolvePathType<PathValueType, Options extends SchemaTypeOptions<PathValueT
IfEquals<PathValueType, ObjectConstructor> extends true ? any:
IfEquals<PathValueType, {}> extends true ? any:
PathValueType extends typeof SchemaType ? PathValueType['prototype'] :
unknown;
unknown;

0 comments on commit 6810555

Please sign in to comment.