Skip to content
Open
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
18 changes: 18 additions & 0 deletions test/types/lean.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,24 @@ async function gh13010() {
expectType<Record<string, string>>(country.name);
}

async function gh13010_1() {
const schema = Schema.create({
name: { required: true, type: Map, of: String }
});

const CountryModel = model('Country', schema);

await CountryModel.create({
name: {
en: 'Croatia',
ru: 'Хорватия'
}
});

const country = await CountryModel.findOne().lean().orFail().exec();
expectType<Record<string, string | undefined>>(country.name);
}

async function gh13345_1() {
const imageSchema = new Schema({
url: { required: true, type: String }
Expand Down
2 changes: 1 addition & 1 deletion test/types/maps.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ function gh10575() {
function gh10872(): void {
const doc = new Test({});

doc.toJSON().map1.foo;
doc.toJSON({ flattenMaps: true }).map1.foo;
}

function gh13755() {
Expand Down
7 changes: 5 additions & 2 deletions test/types/models.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import mongoose, {
WithLevel1NestedPaths,
createConnection,
connection,
model
model,
ObtainSchemaGeneric
} from 'mongoose';
import { expectAssignable, expectError, expectType } from 'tsd';
import { AutoTypedSchemaType, autoTypedSchema } from './schema.test';
Expand Down Expand Up @@ -575,12 +576,14 @@ async function gh12319() {
);

const ProjectModel = model('Project', projectSchema);
const doc = new ProjectModel();
doc.doSomething();

type ProjectModelHydratedDoc = HydratedDocumentFromSchema<
typeof projectSchema
>;

expectType<ProjectModelHydratedDoc>(await ProjectModel.findOne().orFail());
expectAssignable<ProjectModelHydratedDoc>(await ProjectModel.findOne().orFail());
}

function findWithId() {
Expand Down
33 changes: 25 additions & 8 deletions test/types/schema.create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,8 +415,8 @@ export function autoTypedSchema() {
objectId2?: Types.ObjectId | null;
objectId3?: Types.ObjectId | null;
customSchema?: Int8 | null;
map1?: Map<string, string> | null;
map2?: Map<string, number> | null;
map1?: Record<string, string | undefined> | null;
map2?: Record<string, number | undefined> | null;
array1: string[];
array2: any[];
array3: any[];
Expand Down Expand Up @@ -736,17 +736,26 @@ function gh12030() {
} & { _id: Types.ObjectId }>;
} & { _id: Types.ObjectId }>({} as InferSchemaType<typeof Schema3>);

type RawDocType3 = ObtainSchemaGeneric<typeof Schema3, 'DocType'>;
type HydratedDoc3 = ObtainSchemaGeneric<typeof Schema3, 'THydratedDocumentType'>;
expectType<
HydratedDocument<{
users: Types.DocumentArray<
{ credit: number; username?: string | null; } & { _id: Types.ObjectId },
Types.Subdocument<Types.ObjectId, unknown, { credit: number; username?: string | null; } & { _id: Types.ObjectId }> & { credit: number; username?: string | null; } & { _id: Types.ObjectId }
Types.Subdocument<
Types.ObjectId,
unknown,
{ credit: number; username?: string | null; } & { _id: Types.ObjectId }
> & { credit: number; username?: string | null; } & { _id: Types.ObjectId }
>;
} & { _id: Types.ObjectId }>
} & { _id: Types.ObjectId }, {}, {}, {}, RawDocType3>
>({} as HydratedDoc3);
expectType<
Types.Subdocument<Types.ObjectId, unknown, { credit: number; username?: string | null; } & { _id: Types.ObjectId }> & { credit: number; username?: string | null; } & { _id: Types.ObjectId }
Types.Subdocument<
Types.ObjectId,
unknown,
{ credit: number; username?: string | null; } & { _id: Types.ObjectId }
> & { credit: number; username?: string | null; } & { _id: Types.ObjectId }
>({} as HydratedDoc3['users'][0]);

const Schema4 = Schema.create({
Expand Down Expand Up @@ -1166,6 +1175,9 @@ function maps() {
const doc = new Test({ myMap: { answer: 42 } });
expectType<Map<string, number>>(doc.myMap);
expectType<number | undefined>(doc.myMap!.get('answer'));

const obj = doc.toObject();
expectType<Record<string, number | undefined>>(obj.myMap);
}

function gh13514() {
Expand Down Expand Up @@ -1699,7 +1711,12 @@ async function gh14950() {
const doc = await TestModel.findOne().orFail();

expectType<string>(doc.location!.type);
expectType<number[]>(doc.location!.coordinates);
expectType<Types.Array<number>>(doc.location!.coordinates);

const lean = await TestModel.findOne().lean().orFail();

expectType<string>(lean.location!.type);
expectType<number[]>(lean.location!.coordinates);
}

async function gh14902() {
Expand Down Expand Up @@ -1750,7 +1767,7 @@ async function gh14451() {
subdocProp?: string | undefined | null
} | null,
docArr: { nums: number[], times: string[] }[],
myMap?: Record<string, string> | null | undefined,
myMap?: Record<string, string | undefined> | null | undefined,
_id: string
}>({} as TestJSON);
}
Expand Down Expand Up @@ -1860,7 +1877,7 @@ function testInferRawDocTypeFromSchema() {
arr: number[],
docArr: ({ name: string } & { _id: Types.ObjectId })[],
subdoc?: ({ answer: number } & { _id: Types.ObjectId }) | null | undefined,
map?: Map<string, string> | null | undefined
map?: Record<string, string | undefined> | null | undefined;
} & { _id: Types.ObjectId };

expectType<Expected>({} as RawDocType);
Expand Down
35 changes: 23 additions & 12 deletions types/document.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,23 +256,34 @@ declare module 'mongoose' {
set(value: string | Record<string, any>): this;

/** The return value of this method is used in calls to JSON.stringify(doc). */
toJSON(options: ToObjectOptions & { flattenMaps: true, flattenObjectIds: true, virtuals: true }): FlattenMaps<ObjectIdToString<Default__v<Require_id<DocType & TVirtuals>>>>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should those options be listed in ToObjectOptions with documentation?

toJSON(options: ToObjectOptions & { flattenMaps: true, flattenObjectIds: true }): FlattenMaps<ObjectIdToString<Default__v<Require_id<DocType>>>>;
toJSON(options: ToObjectOptions & { flattenMaps: true, virtuals: true }): FlattenMaps<Default__v<Require_id<DocType & TVirtuals>>>;
toJSON(options: ToObjectOptions & { flattenObjectIds: true, virtuals: true }): ObjectIdToString<Default__v<Require_id<DocType & TVirtuals>>>;
toJSON(options: ToObjectOptions & { flattenMaps: true }): FlattenMaps<Default__v<Require_id<DocType>>>;
toJSON(options: ToObjectOptions & { flattenObjectIds: true }): ObjectIdToString<Default__v<Require_id<DocType>>>;
toJSON(options: ToObjectOptions & { virtuals: true }): Default__v<Require_id<DocType & TVirtuals>>;
toJSON(options?: ToObjectOptions & { flattenMaps?: true, flattenObjectIds?: false }): FlattenMaps<Default__v<Require_id<DocType>>>;
toJSON(options: ToObjectOptions & { flattenObjectIds: false }): FlattenMaps<Default__v<Require_id<DocType>>>;
toJSON(options: ToObjectOptions & { flattenObjectIds: true }): ObjectIdToString<FlattenMaps<Default__v<Require_id<DocType>>>>;
toJSON(options: ToObjectOptions & { flattenMaps: false }): Default__v<Require_id<DocType>>;
toJSON(options: ToObjectOptions & { flattenMaps: false; flattenObjectIds: true }): ObjectIdToString<Default__v<Require_id<DocType>>>;

toJSON<T = Default__v<Require_id<DocType>>>(options?: ToObjectOptions & { flattenMaps?: true, flattenObjectIds?: false }): FlattenMaps<T>;
toJSON<T = Default__v<Require_id<DocType>>>(options: ToObjectOptions & { flattenObjectIds: false }): FlattenMaps<T>;
toJSON<T = Default__v<Require_id<DocType>>>(options: ToObjectOptions & { flattenObjectIds: true }): ObjectIdToString<FlattenMaps<T>>;
toJSON<T = Default__v<Require_id<DocType>>>(options: ToObjectOptions & { flattenMaps: false }): T;
toJSON<T = Default__v<Require_id<DocType>>>(options: ToObjectOptions & { flattenMaps: false; flattenObjectIds: true }): ObjectIdToString<T>;
toJSON(options?: ToObjectOptions): Default__v<Require_id<DocType>>;

toJSON<T = Default__v<Require_id<DocType>>>(options: ToObjectOptions & { flattenMaps: true, flattenObjectIds: true }): FlattenMaps<ObjectIdToString<T>>;
toJSON<T = Default__v<Require_id<DocType>>>(options: ToObjectOptions & { flattenObjectIds: true }): ObjectIdToString<T>;
toJSON<T = Default__v<Require_id<DocType>>>(options: ToObjectOptions & { flattenMaps: true }): FlattenMaps<T>;
toJSON<T = Default__v<Require_id<DocType>>>(options?: ToObjectOptions): T;

/** Converts this document into a plain-old JavaScript object ([POJO](https://masteringjs.io/tutorials/fundamentals/pojo)). */
toObject(options: ToObjectOptions & { flattenMaps: true, flattenObjectIds: true, virtuals: true }): FlattenMaps<ObjectIdToString<Default__v<Require_id<DocType & TVirtuals>>>>;
toObject(options: ToObjectOptions & { flattenMaps: true, flattenObjectIds: true }): FlattenMaps<ObjectIdToString<Default__v<Require_id<DocType>>>>;
toObject(options: ToObjectOptions & { flattenMaps: true, virtuals: true }): FlattenMaps<Default__v<Require_id<DocType & TVirtuals>>>;
toObject(options: ToObjectOptions & { flattenObjectIds: true, virtuals: true }): ObjectIdToString<Default__v<Require_id<DocType & TVirtuals>>>;
toObject(options: ToObjectOptions & { flattenMaps: true }): FlattenMaps<Default__v<Require_id<DocType>>>;
toObject(options: ToObjectOptions & { flattenObjectIds: true }): ObjectIdToString<Default__v<Require_id<DocType>>>;
toObject(options: ToObjectOptions & { virtuals: true }): Default__v<Require_id<DocType & TVirtuals>>;
toObject(options?: ToObjectOptions): Default__v<Require_id<DocType>>;
toObject<T>(options?: ToObjectOptions): Default__v<Require_id<T>>;

toObject<T = Default__v<Require_id<DocType>>>(options: ToObjectOptions & { flattenMaps: true, flattenObjectIds: true }): FlattenMaps<ObjectIdToString<T>>;
toObject<T = Default__v<Require_id<DocType>>>(options: ToObjectOptions & { flattenObjectIds: true }): ObjectIdToString<T>;
toObject<T = Default__v<Require_id<DocType>>>(options: ToObjectOptions & { flattenMaps: true }): FlattenMaps<T>;
toObject<T = Default__v<Require_id<DocType>>>(options?: ToObjectOptions): T;

/** Clears the modified state on the specified path. */
unmarkModified<T extends keyof DocType>(path: T): void;
Expand Down
68 changes: 45 additions & 23 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,17 +85,22 @@ declare module 'mongoose' {
collection?: string,
options?: CompileModelOptions
): Model<
InferSchemaType<TSchema>,
ObtainSchemaGeneric<TSchema, 'TQueryHelpers'>,
ObtainSchemaGeneric<TSchema, 'TInstanceMethods'>,
ObtainSchemaGeneric<TSchema, 'TVirtuals'>,
HydratedDocument<
InferSchemaType<TSchema>,
ObtainSchemaGeneric<TSchema, 'TVirtuals'> & ObtainSchemaGeneric<TSchema, 'TInstanceMethods'>,
ObtainSchemaGeneric<TSchema, 'TQueryHelpers'>,
ObtainSchemaGeneric<TSchema, 'TVirtuals'>
>,
TSchema
InferSchemaType<TSchema>,
ObtainSchemaGeneric<TSchema, 'TQueryHelpers'>,
ObtainSchemaGeneric<TSchema, 'TInstanceMethods'>,
ObtainSchemaGeneric<TSchema, 'TVirtuals'>,
// If first schema generic param is set, that means we have an explicit raw doc type,
// so user should also specify a hydrated doc type if the auto inferred one isn't correct.
IsItRecordAndNotAny<ObtainSchemaGeneric<TSchema, 'EnforcedDocType'>> extends true
? ObtainSchemaGeneric<TSchema, 'THydratedDocumentType'>
: HydratedDocument<
InferSchemaType<TSchema>,
ObtainSchemaGeneric<TSchema, 'TVirtuals'> & ObtainSchemaGeneric<TSchema, 'TInstanceMethods'>,
ObtainSchemaGeneric<TSchema, 'TQueryHelpers'>,
ObtainSchemaGeneric<TSchema, 'TVirtuals'>
>,
TSchema,
ObtainSchemaGeneric<TSchema, 'TLeanResultType'>
> & 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 @@ -147,24 +152,26 @@ declare module 'mongoose' {

/** Helper type for getting the hydrated document type from the raw document type. The hydrated document type is what `new MyModel()` returns. */
export type HydratedDocument<
DocType,
HydratedDocPathsType,
TOverrides = {},
TQueryHelpers = {},
TVirtuals = {}
TVirtuals = {},
RawDocType = HydratedDocPathsType
> = IfAny<
DocType,
HydratedDocPathsType,
any,
TOverrides extends Record<string, never> ?
Document<unknown, TQueryHelpers, DocType, TVirtuals> & Default__v<Require_id<DocType>> :
Document<unknown, TQueryHelpers, RawDocType, TVirtuals> & Default__v<Require_id<HydratedDocPathsType>> :
IfAny<
TOverrides,
Document<unknown, TQueryHelpers, DocType, TVirtuals> & Default__v<Require_id<DocType>>,
Document<unknown, TQueryHelpers, DocType, TVirtuals> & MergeType<
Default__v<Require_id<DocType>>,
Document<unknown, TQueryHelpers, RawDocType, TVirtuals> & Default__v<Require_id<HydratedDocPathsType>>,
Document<unknown, TQueryHelpers, RawDocType, TVirtuals> & MergeType<
Default__v<Require_id<HydratedDocPathsType>>,
TOverrides
>
>
>;

export type HydratedSingleSubdocument<
DocType,
TOverrides = {}
Expand Down Expand Up @@ -274,8 +281,9 @@ declare module 'mongoose' {
ObtainDocumentType<any, RawDocType, ResolveSchemaOptions<TSchemaOptions>>,
ResolveSchemaOptions<TSchemaOptions>
>,
THydratedDocumentType = HydratedDocument<FlatRecord<DocType>, TVirtuals & TInstanceMethods, {}, TVirtuals>,
TSchemaDefinition = SchemaDefinition<SchemaDefinitionType<RawDocType>, RawDocType, THydratedDocumentType>
THydratedDocumentType = HydratedDocument<DocType, TVirtuals & TInstanceMethods, {}, TVirtuals>,
TSchemaDefinition = SchemaDefinition<SchemaDefinitionType<RawDocType>, RawDocType, THydratedDocumentType>,
LeanResultType = IsItRecordAndNotAny<RawDocType> extends true ? RawDocType : Default__v<Require_id<BufferToBinary<FlattenMaps<DocType>>>>
>
extends events.EventEmitter {
/**
Expand All @@ -291,7 +299,13 @@ declare module 'mongoose' {
InferRawDocType<TSchemaDefinition, ResolveSchemaOptions<TSchemaOptions>>,
ResolveSchemaOptions<TSchemaOptions>
>,
THydratedDocumentType extends AnyObject = HydratedDocument<InferHydratedDocType<TSchemaDefinition, ResolveSchemaOptions<TSchemaOptions>>>
THydratedDocumentType extends AnyObject = HydratedDocument<
InferHydratedDocType<TSchemaDefinition, ResolveSchemaOptions<TSchemaOptions>>,
TSchemaOptions extends { methods: infer M } ? M : {},
TSchemaOptions extends { query: any } ? TSchemaOptions['query'] : {},
TSchemaOptions extends { virtuals: any } ? TSchemaOptions['virtuals'] : {},
RawDocType
>
>(def: TSchemaDefinition): Schema<
RawDocType,
Model<RawDocType, any, any, any>,
Expand All @@ -305,7 +319,11 @@ declare module 'mongoose' {
ResolveSchemaOptions<TSchemaOptions>
>,
THydratedDocumentType,
TSchemaDefinition
TSchemaDefinition,
ApplySchemaOptions<
InferRawDocType<TSchemaDefinition, ResolveSchemaOptions<TSchemaOptions>, { bufferToBinary: true }>,
ResolveSchemaOptions<TSchemaOptions>
>
>;

static create<
Expand All @@ -329,7 +347,11 @@ declare module 'mongoose' {
ResolveSchemaOptions<TSchemaOptions>
>,
THydratedDocumentType,
TSchemaDefinition
TSchemaDefinition,
ApplySchemaOptions<
InferRawDocType<TSchemaDefinition, ResolveSchemaOptions<TSchemaOptions>, { bufferToBinary: true }>,
ResolveSchemaOptions<TSchemaOptions>
>
>;

/** Adds key path / schema type pairs to this schema. */
Expand Down
Loading
Loading