Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
52c7573
types: avoid FlattenMaps by default on toObject(), toJSON(), lean()
vkarpov15 Jul 5, 2025
da378db
types: add TTransformOptions for InferRawDocType so RawDocType and Le…
vkarpov15 Jul 6, 2025
9340115
Merge branch 'vkarpov15/schema-create' into vkarpov15/gh-13523-3
vkarpov15 Jul 7, 2025
db76c64
fix tests
vkarpov15 Jul 7, 2025
357e0ee
Merge branch 'vkarpov15/schema-create' into vkarpov15/gh-13523-3
vkarpov15 Jul 7, 2025
4cd119f
merge fixes
vkarpov15 Jul 7, 2025
5d49e29
Merge branch 'vkarpov15/schema-create' into vkarpov15/gh-13523-3
vkarpov15 Jul 11, 2025
f21175f
merge conflict cleanup
vkarpov15 Jul 11, 2025
b7a76d5
Merge branch '9.0' into vkarpov15/gh-13523-3
vkarpov15 Oct 13, 2025
c30375d
some merge conflict fixes
vkarpov15 Oct 13, 2025
3b9b6c8
fix THydratedDocumentType params
vkarpov15 Oct 13, 2025
64027dc
fix some missing overrides
vkarpov15 Oct 13, 2025
72eba8c
fix: clean up a couple of more merge conflict issues
vkarpov15 Oct 13, 2025
31778f1
couple of more fixes
vkarpov15 Oct 13, 2025
d565fb1
quick test fix
vkarpov15 Oct 13, 2025
0c7ce68
clean up a couple of more type issues
vkarpov15 Oct 13, 2025
b579111
fix enum handling in InferRawDocType
vkarpov15 Oct 13, 2025
47bb200
fix out of date test
vkarpov15 Oct 13, 2025
5671e58
fix up test inconsistency
vkarpov15 Oct 15, 2025
659c098
types: clean up a couple of more test failures due to missing TVirtuals
vkarpov15 Oct 16, 2025
d2b0ace
use RawDocType in default THydratedDocumentType only if not any
vkarpov15 Oct 16, 2025
4345355
apply id virtual correctly and fix remaining tests
vkarpov15 Oct 16, 2025
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
"test": "mocha --exit ./test/*.test.js",
"test-deno": "deno run --allow-env --allow-read --allow-net --allow-run --allow-sys --allow-write ./test/deno.mjs",
"test-rs": "START_REPLICA_SET=1 mocha --timeout 30000 --exit ./test/*.test.js",
"test-tsd": "node ./test/types/check-types-filename && tsd",
"test-tsd": "node ./test/types/check-types-filename && tsd --full",
"setup-test-encryption": "node scripts/setup-encryption-tests.js",
"test-encryption": "mocha --exit ./test/encryption/*.test.js",
"tdd": "mocha ./test/*.test.js --inspect --watch --recursive --watch-files ./**/*.{js,ts}",
Expand Down
2 changes: 1 addition & 1 deletion test/types/document.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ async function gh15578() {

const schemaOptions = { versionKey: 'taco' } as const;

type ModelType = Model<RawDocType, {}, {}, {}, HydratedDocument<RawDocType, {}, {}, {}, typeof schemaOptions>>;
type ModelType = Model<RawDocType, {}, {}, {}, HydratedDocument<RawDocType, {}, {}, {}, RawDocType, typeof schemaOptions>>;

const ASchema = new Schema<RawDocType, ModelType, {}, {}, {}, {}, typeof schemaOptions>({
testProperty: Number
Expand Down
23 changes: 20 additions & 3 deletions test/types/lean.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Schema, model, Types, InferSchemaType, FlattenMaps, HydratedDocument, Model, Document, PopulatedDoc } from 'mongoose';
import mongoose, { Schema, model, Types, InferSchemaType, FlattenMaps, HydratedDocument, Model, Document, PopulatedDoc } from 'mongoose';
import { expectAssignable, expectError, expectType } from 'tsd';

function gh10345() {
Expand Down Expand Up @@ -47,12 +47,11 @@ async function gh11761() {

console.log({ _id, thing1 });
}
// stretch goal, make sure lean works as well

const foundDoc = await ThingModel.findOne().lean().limit(1).exec();
{
if (!foundDoc) {
return; // Tell TS that it isn't null
return;
}
const { _id, ...thing2 } = foundDoc;
expectType<Types.ObjectId>(foundDoc._id);
Expand Down Expand Up @@ -144,6 +143,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>>(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 @@ -573,12 +574,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
73 changes: 58 additions & 15 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> | null;
map2?: Record<string, number> | 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>>(obj.myMap);
}

function gh13514() {
Expand Down Expand Up @@ -1702,7 +1714,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 @@ -1753,7 +1770,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 @@ -1863,7 +1880,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> | null | undefined;
} & { _id: Types.ObjectId };

expectType<Expected>({} as RawDocType);
Expand All @@ -1881,13 +1898,39 @@ async function testInferHydratedDocTypeFromSchema() {

type HydratedDocType = InferHydratedDocTypeFromSchema<typeof schema>;

type Expected = HydratedDocument<{
name?: string | null | undefined,
arr: Types.Array<number>,
docArr: Types.DocumentArray<{ name: string } & { _id: Types.ObjectId }>,
subdoc?: HydratedDocument<{ answer: number } & { _id: Types.ObjectId }> | null | undefined,
map?: Map<string, string> | null | undefined
} & { _id: Types.ObjectId }>;
type Expected = HydratedDocument<
{
name?: string | null | undefined,
arr: Types.Array<number>,
docArr: Types.DocumentArray<{ name: string } & { _id: Types.ObjectId }>,
subdoc?: HydratedDocument<{ answer: number } & { _id: Types.ObjectId }> | null | undefined,
map?: Map<string, string> | null | undefined
} & { _id: Types.ObjectId },
{},
{},
{},
{
name?: string | null | undefined,
arr: number[],
docArr: Array<{ name: string } & { _id: Types.ObjectId }>,
subdoc?: ({ answer: number } & { _id: Types.ObjectId }) | null | undefined,
map?: Record<string, string> | null | undefined
} & { _id: Types.ObjectId }
>;

expectType<Expected>({} as HydratedDocType);

const def = {
name: String,
arr: [Number],
docArr: [{ name: { type: String, required: true } }],
map: { type: Map, of: String }
} as const;
type InferredHydratedDocType = InferHydratedDocType<typeof def>;
expectType<{
name?: string | null | undefined,
arr?: Types.Array<number> | null | undefined,
docArr?: Types.DocumentArray<{ name: string } & { _id: Types.ObjectId }> | null | undefined,
map?: Map<string, string> | null | undefined
} & { _id: Types.ObjectId }>({} as InferredHydratedDocType);
}
4 changes: 2 additions & 2 deletions test/types/schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2007,13 +2007,13 @@ function testInferHydratedDocTypeFromSchema() {

type HydratedDocType = InferHydratedDocTypeFromSchema<typeof schema>;

type Expected = HydratedDocument<FlatRecord<{
type Expected = HydratedDocument<{
name?: string | null | undefined,
arr: number[],
docArr: Types.DocumentArray<{ name: string }>,
subdoc?: { answer: number } | null | undefined,
map?: Map<string, string> | null | undefined
}>>;
}, { id: string }, {}, { id: string }>;

expectType<Expected>({} as HydratedDocType);
}
Expand Down
29 changes: 19 additions & 10 deletions types/connection.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,16 +186,25 @@ declare module 'mongoose' {
collection?: string,
options?: CompileModelOptions
): Model<
InferSchemaType<TSchema>,
ObtainSchemaGeneric<TSchema, 'TQueryHelpers'>,
ObtainSchemaGeneric<TSchema, 'TInstanceMethods'>,
{},
HydratedDocument<
InferSchemaType<TSchema>,
ObtainSchemaGeneric<TSchema, 'TInstanceMethods'>,
ObtainSchemaGeneric<TSchema, 'TQueryHelpers'>
>,
TSchema> & ObtainSchemaGeneric<TSchema, 'TStaticMethods'>;
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'>,
InferSchemaType<TSchema>,
ObtainSchemaGeneric<TSchema, 'TSchemaOptions'>
>,
TSchema,
ObtainSchemaGeneric<TSchema, 'TLeanResultType'>
> & ObtainSchemaGeneric<TSchema, 'TStaticMethods'>;
model<T, U, TQueryHelpers = {}>(
name: string,
schema?: Schema<T, any, any, TQueryHelpers, any, any, any>,
Expand Down
Loading
Loading