Open juona opened 1 year ago
"My understanding is that this utility should produce a plain type that has nothing to do with Mongoose": Ideally yes, the Types.DocumentArray<>
workaround is one exception to that rule. We're trying to change that for 8.0.
As expected, going to be a bit hard to change this without #13856. Because InferSchemaType<>
can't quite decide whether it wants to return the raw document type or the hydrated document type. And we don't want to do type transformations in HydratedDocument<>
because of issues like #13523: mapping types in TypeScript get screwed up by generics, private fields, etc.
any workaround for this one?
Not currently, we're working on a workaround.
Going to move this out of 8.0 to a future release with the work we're doing on #13900. We haven't been able to make progress on this issue, and I don't want this to block 8.0 any further. Plus the work from #13900 is something we can release in a minor release.
My solution :
const likeSchema = new Schema(
{
userId: { type: String, required: true },
date: { type: Date, required: true },
},
{
timestamps: true,
_id: true,
}
);
const postSchema = new Schema(
{
title: { type: String, required: true },
content: { type: String, required: true },
likes: { type: [likeSchema], required: true, default: [] },
},
{
timestamps: true,
_id: true,
}
);
const userSchema = new Schema(
{
_id: { type: mongoose.Schema.Types.ObjectId, required: true },
name: { type: String, required: true },
lastName: { type: String, required: true },
settings: {
type: {
isPublic: { type: Boolean, required: true, default: false },
isForSale: { type: Boolean, required: true, default: false },
},
required: true,
default: {},
},
age: { type: Number, required: false },
posts: {
type: [postSchema],
required: true,
default: [],
},
},
{
timestamps: true,
_id: true,
}
);
type ReplaceMongooseDocumentArrayByArray<MySchema extends Record<string, any>> = {
[K in keyof MySchema]: MySchema[K] extends mongoose.Types.DocumentArray<infer ArrayType>
? ReplaceMongooseDocumentArrayByArray<Omit<ArrayType, keyof mongoose.Types.Subdocument>>[]
: MySchema[K] extends number | string | boolean | Date | mongoose.Schema.Types.ObjectId | mongoose.Types.ObjectId
? MySchema[K]
: ReplaceMongooseDocumentArrayByArray<MySchema[K]>;
};
type User = ReplaceMongooseDocumentArrayByArray<InferSchemaType<typeof userSchema>>;
Also came across this issue. I'm trying to use InferSchemaType
to generate TS types for models that contain nested subdocuments. I have a suspicion it's only working for the top level when it should be recursing down into subdocuments if they exist.
Consider the following code:
import * as mongoose from 'mongoose'
const childSchema = new mongoose.Schema({ name: String });
const parentSchema = new mongoose.Schema({
// Array of subdocuments
children: [childSchema],
// Single nested subdocuments
child: childSchema
});
type ParentSchema = mongoose.InferSchemaType<typeof parentSchema>;
const Parent = mongoose.model('Parent', parentSchema);
function create(parent: ParentSchema) {
return Parent.create(parent)
}
create({ child: { name: 'child' }, children: [{ name: 'another-child' }] })
This has a TS error when creating the children[]:
The type that comes out of InferSchemaType looks like this:
type ParentSchema = {
children: mongoose.Types.DocumentArray<{
name?: string | null | undefined;
}>;
child?: {
name?: string | null | undefined;
} | null | undefined;
}
The generated type should look like this:
type ParentSchema = {
children: {
name?: string | null | undefined;
}[];
child?: {
name?: string | null | undefined;
} | null | undefined;
};
You can work around this issue by doing the following:
type ParentSchema = Omit<mongoose.InferSchemaType<typeof parentSchema>, 'children'> & { children: mongoose.InferSchemaType<typeof childSchema>[] };
I can take a stab at fixing if you point me to where in the code I might look for this. Thanks in advance!
@domharrington I don't think you should use InferSchemaType
for this purpose. Use InferRawDocType
instead to get the schema's raw doc type as follows:
import * as mongoose from 'mongoose'
const childSchema = new mongoose.Schema({ name: String });
const parentSchemaDef = {
// Array of subdocuments
children: [childSchema],
// Single nested subdocuments
child: childSchema
};
const parentSchema = new mongoose.Schema(parentSchemaDef);
type ParentSchema = mongoose.InferRawDocType<typeof parentSchemaDef>;
const Parent = mongoose.model('Parent', parentSchema);
function create(parent: ParentSchema) {
return Parent.create(parent)
}
create({ child: { name: 'child' }, children: [{ name: 'another-child' }] })
InferrRawDocType
is more correct here because you want the POJO type from the schema definition, InferSchemaType
is more to support Mongoose's internal automatic type inference.
Oh interesting! I did not know about that one. That seems to do the trick, thank you!
I tried refactoring my code to use InferRawDocType
instead of InferSchemaType
but now I'm coming up against another error (i think it's the same as this one to do with Dates: https://github.com/Automattic/mongoose/issues/14839 which it seems like has just had a fix merged in over here).
I've subscribed to that issue and will wait until that version gets released before trying again. The error I'm getting is this:
The types of 'toJSON(...).completedAt' are incompatible between these types.
Type 'Date | null | undefined' is not assignable to type 'FlattenMaps<Date> | null | undefined'.
Type 'Date' is missing the following properties from type 'FlattenMaps<Date>': expires, max, min, defaultOptions, and 21 more.ts(2345)
Which I think is the same!
Yeah that looks like the same issue as #14839. We will ship a fix for that this week.
Yeah that looks like the same issue as #14839. We will ship a fix for that this week.
Thanks in advance! Appreciate your work on this as always.
@domharrington your issue should be fixed in v8.6.3
Prerequisites
Mongoose version
7.4.4
Node.js version
16.19.1
MongoDB server version
5
Typescript version (if applicable)
4.8.3
Description
The
InferSchemaType
utility converts subdocument arrays intoDocumentArray
s. My understanding is that this utility should produce a plain type that has nothing to do with Mongoose, so it should infer simple arrays instead ofDocumentArray
s. Is my expectation incorrect?Possibly related: https://github.com/Automattic/mongoose/issues/12030 .
Steps to Reproduce
Type
IMySchema
is:Expected Behavior
I'd expect type
IMySchema
to be: