Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

types: avoid unnecessary MergeType<> if TOverrides not set, clean up statics and insertMany() type issues #13577

Merged
merged 5 commits into from
Jul 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions test/types/models.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { ObjectId } from 'bson';
import {
Schema,
Document,
Expand Down Expand Up @@ -77,7 +76,7 @@
});

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

const res2 = await Test.insertMany([{ foo: 'bar' }], { ordered: false, rawResult: true });
expectAssignable<Error | Object | ReturnType<(typeof Test)['hydrate']>>(res2.mongoose.results[0]);
Expand Down Expand Up @@ -622,3 +621,21 @@
expectType<ChangeStreamInsertDocument<ITest>>(change);
});
}

function gh13529() {
interface ResourceDoc {
foo: string;
}

type ResourceIdT<ResourceIdField extends string> = {
[key in ResourceIdField]: string;
};
type ResourceDocWithId<ResourceIdField extends string> = ResourceDoc & ResourceIdT<ResourceIdField>;

function test<
ResourceType extends string,
DocT extends ResourceDocWithId<`${ResourceType}Id`>>(dbModel: Model<DocT>) {
const resourceDoc = new dbModel();
resourceDoc.foo = 'bar';
}
}

Check warning on line 641 in test/types/models.test.ts

View workflow job for this annotation

GitHub Actions / Lint TS-Files

Newline required at end of file but not found
6 changes: 3 additions & 3 deletions test/types/virtuals.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Document, Model, Schema, model, InferSchemaType, FlatRecord, ObtainSchemaGeneric } from 'mongoose';
import { Document, Model, Schema, model, InferSchemaType, ObtainSchemaGeneric } from 'mongoose';
import { expectType } from 'tsd';

interface IPerson {
Expand Down Expand Up @@ -90,7 +90,7 @@ function gh11543() {
async function autoTypedVirtuals() {
type AutoTypedSchemaType = InferSchemaType<typeof testSchema>;
type VirtualsType = { domain: string };
type InferredDocType = FlatRecord<AutoTypedSchemaType & ObtainSchemaGeneric<typeof testSchema, 'TVirtuals'>>;
type InferredDocType = AutoTypedSchemaType & ObtainSchemaGeneric<typeof testSchema, 'TVirtuals'>;

const testSchema = new Schema({
email: {
Expand Down Expand Up @@ -118,7 +118,7 @@ async function autoTypedVirtuals() {
const doc = new TestModel();
expectType<string>(doc.domain);

expectType<FlatRecord<AutoTypedSchemaType & VirtualsType >>({} as InferredDocType);
expectType<AutoTypedSchemaType & VirtualsType>({} as InferredDocType);

const doc2 = await TestModel.findOne().orFail();
expectType<string>(doc2.domain);
Expand Down
45 changes: 25 additions & 20 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,16 @@
> = IfAny<
DocType,
any,
Document<unknown, TQueryHelpers, DocType> & MergeType<
Require_id<DocType>,
TOverrides extends Record<string, never> ?
{} :
IfAny<TOverrides, {}>
>
TOverrides extends Record<string, never> ?
Document<unknown, TQueryHelpers, DocType> & Require_id<DocType> :
IfAny<
TOverrides,
Document<unknown, TQueryHelpers, DocType> & Require_id<DocType>,
Document<unknown, TQueryHelpers, DocType> & MergeType<
Require_id<DocType>,
TOverrides
>
>
>;
export type HydratedSingleSubdocument<DocType, TOverrides = {}> = Types.Subdocument<unknown> & Require_id<DocType> & TOverrides;
export type HydratedArraySubdocument<DocType, TOverrides = {}> = Types.ArraySubdocument<unknown> & Require_id<DocType> & TOverrides;
Expand Down Expand Up @@ -212,7 +216,7 @@

export class Schema<
EnforcedDocType = any,
M = Model<EnforcedDocType, any, any, any>,
TModelType = Model<EnforcedDocType, any, any, any>,
TInstanceMethods = {},
TQueryHelpers = {},
TVirtuals = {},
Expand All @@ -225,13 +229,13 @@
ObtainDocumentType<any, EnforcedDocType, ResolveSchemaOptions<TSchemaOptions>>,
ResolveSchemaOptions<TSchemaOptions>
>,
THydratedDocumentType = HydratedDocument<FlatRecord<DocType>, TVirtuals & TInstanceMethods>
THydratedDocumentType = HydratedDocument<DocType, TVirtuals & TInstanceMethods>
>
extends events.EventEmitter {
/**
* Create a new schema
*/
constructor(definition?: SchemaDefinition<SchemaDefinitionType<EnforcedDocType>> | DocType, options?: SchemaOptions<FlatRecord<DocType>, TInstanceMethods, TQueryHelpers, TStaticMethods, TVirtuals, THydratedDocumentType> | ResolveSchemaOptions<TSchemaOptions>);
constructor(definition?: SchemaDefinition<SchemaDefinitionType<EnforcedDocType>> | DocType, options?: SchemaOptions<DocType, TInstanceMethods, TQueryHelpers, TStaticMethods, TVirtuals, THydratedDocumentType> | ResolveSchemaOptions<TSchemaOptions>);

/** Adds key path / schema type pairs to this schema. */
add(obj: SchemaDefinition<SchemaDefinitionType<EnforcedDocType>> | Schema, prefix?: string): this;
Expand Down Expand Up @@ -311,7 +315,7 @@
pathType(path: string): string;

/** Registers a plugin for this schema. */
plugin<PFunc extends PluginFunction<DocType, M, any, any, any, any>, POptions extends Parameters<PFunc>[1] = Parameters<PFunc>[1]>(fn: PFunc, opts?: POptions): this;
plugin<PFunc extends PluginFunction<DocType, TModelType, any, any, any, any>, POptions extends Parameters<PFunc>[1] = Parameters<PFunc>[1]>(fn: PFunc, opts?: POptions): this;

/** Defines a post hook for the model. */

Expand All @@ -320,7 +324,7 @@
post<T = Query<any, any>>(method: MongooseQueryMiddleware | MongooseQueryMiddleware[] | RegExp, options: SchemaPostOptions & { errorHandler: true }, fn: ErrorHandlingMiddlewareWithOption<T>): this;
post<T = THydratedDocumentType>(method: MongooseDocumentMiddleware | MongooseDocumentMiddleware[] | RegExp, options: SchemaPostOptions & { errorHandler: true }, fn: ErrorHandlingMiddlewareWithOption<T>): this;
post<T extends Aggregate<any>>(method: 'aggregate' | RegExp, options: SchemaPostOptions & { errorHandler: true }, fn: ErrorHandlingMiddlewareWithOption<T, Array<any>>): this;
post<T = M>(method: 'insertMany' | RegExp, options: SchemaPostOptions & { errorHandler: true }, fn: ErrorHandlingMiddlewareWithOption<T>): this;
post<T = TModelType>(method: 'insertMany' | RegExp, options: SchemaPostOptions & { errorHandler: true }, fn: ErrorHandlingMiddlewareWithOption<T>): this;

// this = never since it never happens
post<T = never>(method: MongooseQueryOrDocumentMiddleware | MongooseQueryOrDocumentMiddleware[] | RegExp, options: SchemaPostOptions & { document: false, query: false }, fn: PostMiddlewareFunction<never, never>): this;
Expand Down Expand Up @@ -358,14 +362,14 @@
// method aggregate and insertMany with PostMiddlewareFunction
post<T extends Aggregate<any>>(method: 'aggregate' | RegExp, fn: PostMiddlewareFunction<T, Array<AggregateExtract<T>>>): this;
post<T extends Aggregate<any>>(method: 'aggregate' | RegExp, options: SchemaPostOptions, fn: PostMiddlewareFunction<T, Array<AggregateExtract<T>>>): this;
post<T = M>(method: 'insertMany' | RegExp, fn: PostMiddlewareFunction<T, T>): this;
post<T = M>(method: 'insertMany' | RegExp, options: SchemaPostOptions, fn: PostMiddlewareFunction<T, T>): this;
post<T = TModelType>(method: 'insertMany' | RegExp, fn: PostMiddlewareFunction<T, T>): this;
post<T = TModelType>(method: 'insertMany' | RegExp, options: SchemaPostOptions, fn: PostMiddlewareFunction<T, T>): this;

// method aggregate and insertMany with ErrorHandlingMiddlewareFunction
post<T extends Aggregate<any>>(method: 'aggregate' | RegExp, fn: ErrorHandlingMiddlewareFunction<T, Array<any>>): this;
post<T extends Aggregate<any>>(method: 'aggregate' | RegExp, options: SchemaPostOptions, fn: ErrorHandlingMiddlewareFunction<T, Array<any>>): this;
post<T = M>(method: 'insertMany' | RegExp, fn: ErrorHandlingMiddlewareFunction<T>): this;
post<T = M>(method: 'insertMany' | RegExp, options: SchemaPostOptions, fn: ErrorHandlingMiddlewareFunction<T>): this;
post<T = TModelType>(method: 'insertMany' | RegExp, fn: ErrorHandlingMiddlewareFunction<T>): this;
post<T = TModelType>(method: 'insertMany' | RegExp, options: SchemaPostOptions, fn: ErrorHandlingMiddlewareFunction<T>): this;

/** Defines a pre hook for the model. */
// this = never since it never happens
Expand All @@ -390,8 +394,8 @@
pre<T extends Aggregate<any>>(method: 'aggregate' | RegExp, fn: PreMiddlewareFunction<T>): this;
pre<T extends Aggregate<any>>(method: 'aggregate' | RegExp, options: SchemaPreOptions, fn: PreMiddlewareFunction<T>): this;
/* method insertMany */
pre<T = M>(method: 'insertMany' | RegExp, fn: (this: T, next: (err?: CallbackError) => void, docs: any | Array<any>) => void | Promise<void>): this;
pre<T = M>(method: 'insertMany' | RegExp, options: SchemaPreOptions, fn: (this: T, next: (err?: CallbackError) => void, docs: any | Array<any>) => void | Promise<void>): this;
pre<T = TModelType>(method: 'insertMany' | RegExp, fn: (this: T, next: (err?: CallbackError) => void, docs: any | Array<any>) => void | Promise<void>): this;
pre<T = TModelType>(method: 'insertMany' | RegExp, options: SchemaPreOptions, fn: (this: T, next: (err?: CallbackError) => void, docs: any | Array<any>) => void | Promise<void>): this;

/** Object of currently defined query helpers on this schema. */
query: TQueryHelpers;
Expand All @@ -413,11 +417,12 @@

/** Adds static "class" methods to Models compiled from this schema. */
static<K extends keyof TStaticMethods>(name: K, fn: TStaticMethods[K]): this;
static(obj: { [F in keyof TStaticMethods]: TStaticMethods[F] } & { [name: string]: (this: M, ...args: any[]) => any }): this;
static(name: string, fn: (this: M, ...args: any[]) => any): this;
static(obj: { [F in keyof TStaticMethods]: TStaticMethods[F] } & { [name: string]: (this: TModelType, ...args: any[]) => any }): this;
static(name: string, fn: (this: TModelType, ...args: any[]) => any): this;

/** Object of currently defined statics on this schema. */
statics: { [F in keyof TStaticMethods]: TStaticMethods[F] } & { [name: string]: (this: M, ...args: any[]) => any };
statics: { [F in keyof TStaticMethods]: TStaticMethods[F] } &
{ [name: string]: (this: TModelType, ...args: any[]) => unknown };

Check warning on line 425 in types/index.d.ts

View workflow job for this annotation

GitHub Actions / Lint TS-Files

Expected indentation of 4 spaces but found 6

/** Creates a virtual type with the given name. */
virtual<T = HydratedDocument<DocType, TVirtuals & TInstanceMethods, TQueryHelpers>>(
Expand Down
33 changes: 17 additions & 16 deletions types/models.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,46 +336,47 @@ declare module 'mongoose' {
insertMany<DocContents = TRawDocType>(
docs: Array<DocContents | TRawDocType>,
options: InsertManyOptions & { lean: true; }
): Promise<Array<MergeType<MergeType<TRawDocType, DocContents>, Require_id<TRawDocType>>>>;
): Promise<Array<Require_id<DocContents>>>;
insertMany<DocContents = TRawDocType>(
doc: DocContents,
docs: DocContents | TRawDocType,
options: InsertManyOptions & { lean: true; }
): Promise<Array<Require_id<DocContents>>>;
insertMany<DocContents = TRawDocType>(
doc: DocContents | TRawDocType,
options: InsertManyOptions & { ordered: false; rawResult: true; }
): Promise<mongodb.InsertManyResult<TRawDocType> & {
): Promise<mongodb.InsertManyResult<Require_id<DocContents>> & {
mongoose: {
validationErrors: Error[];
results: Array<
Error |
Object |
HydratedDocument<
MergeType<
MergeType<TRawDocType, DocContents>,
Require_id<TRawDocType>
>
>
MergeType<THydratedDocumentType, DocContents>
>
}
}>;
insertMany<DocContents = TRawDocType>(
docs: Array<DocContents | TRawDocType>,
options: InsertManyOptions & { rawResult: true; }
): Promise<mongodb.InsertManyResult<TRawDocType>>;
): Promise<mongodb.InsertManyResult<Require_id<DocContents>>>;
insertMany<DocContents = TRawDocType>(
docs: Array<DocContents | TRawDocType>
): Promise<Array<HydratedDocument<MergeType<MergeType<TRawDocType, DocContents>, Require_id<TRawDocType>>, TVirtuals & TInstanceMethods, TQueryHelpers>>>;
): Promise<Array<MergeType<THydratedDocumentType, Omit<DocContents, '_id'>>>>;
insertMany<DocContents = TRawDocType>(
doc: DocContents,
options: InsertManyOptions & { lean: true; }
): Promise<Array<MergeType<MergeType<TRawDocType, DocContents>, Require_id<TRawDocType>>>>;
): Promise<Array<Require_id<DocContents>>>;
insertMany<DocContents = TRawDocType>(
doc: DocContents,
options: InsertManyOptions & { rawResult: true; }
): Promise<mongodb.InsertManyResult<TRawDocType>>;
): Promise<mongodb.InsertManyResult<Require_id<DocContents>>>;
insertMany<DocContents = TRawDocType>(
doc: DocContents,
options: InsertManyOptions
): Promise<Array<HydratedDocument<MergeType<MergeType<TRawDocType, DocContents>, Require_id<TRawDocType>>, TVirtuals & TInstanceMethods, TQueryHelpers>>>;
insertMany<DocContents = TRawDocType>(doc: DocContents): Promise<
Array<HydratedDocument<MergeType<MergeType<TRawDocType, DocContents>, Require_id<TRawDocType>>, TVirtuals & TInstanceMethods, TQueryHelpers>>
): Promise<Array<MergeType<THydratedDocumentType, Omit<DocContents, '_id'>>>>;
insertMany<DocContents = TRawDocType>(
doc: DocContents
): Promise<
Array<MergeType<THydratedDocumentType, Omit<DocContents, '_id'>>>
>;

/** The name of the model */
Expand Down
8 changes: 4 additions & 4 deletions types/schemaoptions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,10 +204,10 @@ declare module 'mongoose' {
* Model Statics methods.
*/
statics?: IfEquals<
TStaticMethods,
{},
Record<any, (this: Model<DocType>, ...args: any) => unknown>,
TStaticMethods
TStaticMethods,
{},
{ [name: string]: (this: Model<DocType>, ...args: any[]) => unknown },
{ [F in keyof TStaticMethods]: TStaticMethods[F] }
>

/**
Expand Down
14 changes: 7 additions & 7 deletions types/utility.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@
* @description It makes intellisense dialog box easier to read as a single object instead of showing that in multiple object unions.
* @param {T} T The type to be converted.
*/
type FlatRecord<T> = { [K in keyof T]: T[K] };
type FlatRecord<T> = { [K in keyof T]: T[K] };

/**

Check warning on line 24 in types/utility.d.ts

View workflow job for this annotation

GitHub Actions / Lint TS-Files

Expected indentation of 2 spaces but found 3
* @summary Checks if a type is "Record" or "any".
* @description It Helps to check if user has provided schema type "EnforcedDocType"
* @param {T} T A generic type to be checked.
* @returns true if {@link T} is Record OR false if {@link T} is of any type.
*/
type IsItRecordAndNotAny<T> = IfEquals<T, any, false, T extends Record<any, any> ? true : false>;
* @summary Checks if a type is "Record" or "any".
* @description It Helps to check if user has provided schema type "EnforcedDocType"
* @param {T} T A generic type to be checked.
* @returns true if {@link T} is Record OR false if {@link T} is of any type.
*/
type IsItRecordAndNotAny<T> = IfEquals<T, any, false, T extends Record<any, any> ? true : false>;

/**
* @summary Checks if two types are identical.
Expand Down