-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
feat(types+schema): allow defining schema paths using mongoose.Types.* to work around TS type inference issues #12352
Conversation
…* to work around TS type inference issues Fix #12205
Yes, that seems to be correct, I've not checked the changes you made in this PR, but I've noticed that we can solve it as well by making this change "Infer doc type directly without using // inferschematype.d.ts
// line 41
- type InferSchemaType<SchemaType> = ObtainSchemaGeneric<SchemaType, 'DocType'>;
+ type InferSchemaType<SchemaType> = SchemaType extends Schema<any, any, any, any, any, any, any, infer T> ? T : unknown; OR by making the following change which I mentioned before in #12168: // inferschematype.d.ts
// line 171
-PathValueType extends Record<string, any> ? ObtainDocumentType<PathValueType, any, TypeKey> :
+PathValueType extends Record<string, any> ? IfEquals<PathValueType, Types.ObjectId, Types.ObjectId, ObtainDocumentType<PathValueType, any, TypeKey>>: After using TS for a while, sometimes it feels that TS can have unexpected behavior, for instance in the first solution Line 175 in 0af4cb8
ObtainDocumentType multiple time in the code and for be able to show the schema paths when user hovers on schema itself.I don't really know right now how TS utilities works in details, "I've not read TS source code yet 😄" But I think it might be helpful if we ask someone of TS team about this specific case, It might be helpful to understand the TS behavior more. @vkarpov15 can you mention someone here of TS team who can be able to help here quickly? I don't think we can find the answer in its docs. "I might be wrong" |
I don't know of anyone on the TS team that might help with this. I spent a long time wrangling with the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
I will check that at the weekend. |
@@ -640,6 +644,28 @@ function gh12030() { | |||
] | |||
}); | |||
|
|||
type A = ResolvePathType<[ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these tests are a bit wrong, I think they should be:
const a = [
{
username: { type: String }
}
];
expectType<{
username?: string
}[]>({} as ResolvePathType<typeof a>);
const b = {
users: [
{
username: { type: String }
}
]
};
expectType<{
users: {
username?: string
}[];
}>({} as ResolvePathType<typeof b>);
const Schema1 = new Schema({
users: [
{
username: { type: String }
}
]
});
PathValueType extends typeof SchemaType ? PathValueType['prototype'] : | ||
PathValueType extends Record<string, any> ? ObtainDocumentType<PathValueType, any, TypeKey> : | ||
unknown; | ||
IfEquals<PathValueType, Schema.Types.String> extends true ? PathEnumOrString<Options['enum']> : |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need to be more careful before making changes to ResolvePathType
,
I've not checked all changes yet, but I prefer to avoid making all of these changes. " I might be wrong"
Will come back with more details.
const campaignSchema = new Schema( | ||
{ | ||
client: { | ||
type: new Types.ObjectId(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is ObjectId instance an acceptable value to pass ?
I've not a lot of time to check this, but I've left some comments, I will get more time after 12 SEP. |
Guys, I think I figured out the solution: |
Any news? Do I have to create another PR to address this? |
I think that is incorrect, I think the entire issue as @vkarpov15 mentioned that InferSchemaType reinfer the already inferred type. I might misunderstand you, but check this example first: |
In ResolvePathType helper, we can do this for fixing ObjectId issue: -PathValueType extends ObjectIdSchemaDefinition ? Types.ObjectId :
+PathValueType extends ObjectIdSchemaDefinition | Types.ObjectId ? Types.ObjectId : And the same for the other mentioned types. |
* @example | ||
* const userSchema = new Schema({userName:String}); | ||
* type UserType = InferSchemaType<typeof userSchema>; | ||
* // result | ||
* type UserType = {userName?: string} | ||
*/ | ||
type InferSchemaType<SchemaType> = ObtainSchemaGeneric<SchemaType, 'DocType'>; | ||
type InferSchemaType<TSchema> = ObtainSchemaGeneric<TSchema, 'DocType'>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you like to test this ?
note: revert all changes in this file while you testing.
type InferSchemaType<TSchema> = TSchema extends Schema<any, any, any, any, any, any, string, infer DocType> ? DocType : unknown;
|
||
expectType<'type'>({} as ObtainSchemaGeneric<typeof campaignSchema, 'TPathTypeKey'>); | ||
|
||
type A = ObtainDocumentType<{ client: { type: Schema.Types.ObjectId, required: true } }>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You use Schema.Types.ObjectId
as type, I think this is incorrect, or I might be wrong.
Same thing for the following tests.
Fix #12205
Summary
I've spent quite some time debugging #12205 and haven't come up with quite a complete picture of why this is happening, but I got a decent enough workaround.
The issue comes down to the fact that, for some reason,
InferSchemaType<>
runs theObtainDocumentType
calculation twice: once with the schema definition, and once with the returned value from the first run. I suspect that's because of the self-referentialDocType = ObtainDocumentType<DocType>
line here, but I haven't been able to figure out how to remove that. Any thoughts on this issue @mohammad0-0ahmad ?Also had to add some fixes to
ResolvePathType<>
because, apparently,Schema.Types.ObjectId extends typeof Schema.Types.ObjectId
is false 🤷♂️ .The workaround I came up with is to allow defining ObjectId and Decimal128 paths using
mongoose.Types.ObjectId
in addition tomongoose.Schema.Types.ObjectId
(and same for Decimal128). This is likely a decent DX improvement anyway, since the difference betweenmongoose.Types.ObjectId
andmongoose.Schema.Types.ObjectId
can be confusing.Examples