Skip to content

Commit

Permalink
Merge pull request #13577 from Automattic/vkarpov15/gh-13529
Browse files Browse the repository at this point in the history
types: avoid unnecessary MergeType<> if TOverrides not set, clean up statics and insertMany() type issues
  • Loading branch information
vkarpov15 committed Jul 10, 2023
2 parents e94ca23 + 8b8f37b commit 2188458
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 52 deletions.
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 @@ async function insertManyTest() {
});

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 @@ function gh13206() {
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 @@ declare module 'mongoose' {
> = 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 @@ declare module 'mongoose' {

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 @@ declare module 'mongoose' {
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 @@ declare module 'mongoose' {
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 @@ declare module 'mongoose' {
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 @@ declare module 'mongoose' {
// 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 @@ declare module 'mongoose' {
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 @@ declare module 'mongoose' {

/** 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 @@ declare module 'mongoose' {
* @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

0 comments on commit 2188458

Please sign in to comment.