-
-
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
LeanDocument Type doesn't include _id or id #11761
Comments
I think, that id is not on a lean document if you did not define id in the schema explicitly, as id is actually a virtual field. |
I guess you can avoid the bug for now, by explicitly defining _id: Schema.Types.ObjectId in your Schema. |
Yeah, I'm just That said, I don't have super deep understanding of the inner workings of all this and I could be missing something. 🤷♂️ It's why I raised a bug rather than open an MR. Didn't feel comfortable mucking about on this one, haha. Thanks for taking a look at it! 😁 |
This is one of those things which could be fixed, but there are a lot of potential issues that it can cause; I would strongly suggest that you just define _id yourself in your schema. It's a simple one-time fix and it removes a lot of complexity in the typescript |
This is a clear regression. This is comparable to when Apple had antenna issues with their iphones, if you hold the IPhones in your hand and they basically said, that it is the consumers fault, because the consumer hold the iPhone "wrong"... This is not acceptable. @mohammad0-0ahmad |
I've tested the code snippet that is provided by @noseworthy, but I couldn't reproduce TS warning because the returned value is "any". |
Sorry, I'm looking more closely at this and there are several things that don't make sense. First, the assumption stated in the issue is incorrect: "This says that LeanDocuments (that is documents returned from .toObject() or when using the lean option don't have the _id or id fields." -- that's actually exactly opposite. The reference to _id is in a double negative -- it's telling it to omit any fields from Document except those ones. I'm not claiming that there is no issue here because clearly someone is seeing an issue, but I can't reproduce it either because -- like @mohammad0-0ahmad says -- the type here is |
The regression has appeared after merging this commit e4b007b3fb2f6a92a4e33579f3757b8964613b1f |
@taxilian I think he forgot to provide schema type as generic in the code snippet as I said above. |
I very very strongly recommend against making it add _id in that case. it's up to you, but in the long run it's going to add a nontrivial amount of complexity which far exceeds the problems that it solves. The benefit is not worth the cost. |
I respectfully disagree. We dont break user space without making a major release. Now we force devs to patch their schemas or write ts-ignore. Also it would mean that the typings do not represent the javascript reality. @mohammad0-0ahmad Would the autotyping PR fix this implicitly? |
Not sure if this is strictly related, but the change to the types here also broke some stuff for us. In particular making the return type of The most confusing thing is that this is now actually inconsistent between |
Hello @taxilian ! |
Hi @ghost91- ! |
I've created new PR instead.😉 |
This is a dumbed down example of what we are doing: class GenericRepository<T extends { id: string }> {
constructor(private readonly GenericModel: Model<T>) {}
public async create(t: T): Promise<T> {
const document = new this.GenericModel(t);
await document.save();
return document.toObject<T>(); // worked in 6.3.1, broken in 6.3.2, as it returns `LeanDocument<T>` instead of `T` now, which might not be compatible with `T`
}
public async update(t: T): Promise<T | null> {
const document = await this.GenericModel.findOneAndUpdate({ id: t.id }, t).lean<T | null>(); // still OK in 6.3.2
return document;
}
} The underlying problem is that TypeScript doesn't know that I realize that the new way may actually be a little more correct (not sure what would happen at runtime if a schema defined properties that clash with things added by |
After merging mentioned PR, you will be able to do like following:
What do you think about that @ghost91-? |
The point is that we want to return Maybe to give a bit of context on why we don't want to use Anyways, I still think const object = await this.GenericModel.findOneAndUpdate({ id: t.id }, t).lean<T | null>(); // currently of type T | null and const object = (await this.GenericModel.findOneAndUpdate({ id: t.id }, t)).toObject<T | null>(); // currently of type LeanDocument<T | null> ? |
@ghost91- |
Woah, this blew up over night. Thanks for looking at it folks and sorry for the incomplete and misleading issue description. You're right, I was missing the schema type generic and totally misread that |
oh please please please don't use that PR (#11767) -- that undoes literally days of work. If this is that big of a concern then I will find a way to fix it. That PR oversimplifies to the point that you fix this one issue and create lots and lots of other ones. Let me take a look at this and fix it the right way, not by just undoing a bunch of needed things |
At this point I'm not sure I 100% understand the things people are trying to do -- if you can give me a couple of tests which fail right now and should not fail I will make them work without completely neutering the improvements that I made. Let's please not throw the baby out with the bathwater. It's very possible -- and quite easy -- to have types which are actually useful and help you locate code which is going to be problematic. It frustrates me that we're so determined to have code work which seemed intuitive because of weird assumptions that we're willing to throw out usefulness as well, but if it's that important then let's fix it -- but let's fix it, not just say "well, that improvement made things harder so forget it". If these are things which are important then let's get tests around them to make sure they don't break and let's fix them. |
Sorry, responding in a more reasoned way now =] If we have to do this as a "quick fix" I understand.
Those are 100% not unnecessary types. They are very necessary to cover some very specific cases while still allowing useful types. I'm happy to discuss specifics, though not sure if this is the most useful place to do so -- are you on the mongoose slack? Just let me know when/how you want to discuss. |
Maybe we should really do it the strict TDD was and collect typings tests, and then implement the typings. |
I am not on the mongoose slack. |
I've been trying to do that with all my changes -- working on a fix for this doing that method. That's one reason that I'm concerned that the largest change in the proposed PR involves removing 40 lines of unit test code which would no longer work.... |
Not quite sure if it is the same, but 6.3.2 causes also a lot of troulbes on our side. toObject() seems to not match the plain type anymore? e.g.
or |
They are working if I keep them, but there are some types that are used in these tests and doesn't use internally. We can discuss that further in the mentioned PR instead of here. |
Moving discussion there =] |
@simllll is there any way you can make a short example showing the problem? I'm happy to take a look at it but I'm not following your explanation. If you can give me a snippet I can put in a unit test we can a) address it and b) make sure it doesn't come back. |
@taxilian Nothing special actually, what we do is for example something like this: MyModel: interface IDBMyModel extends Document {
_id: string;
someprop: string;
}
const MyModel: Model<IDBMyModel> = model<IDBMyModel>(
'MyModel',
new Schema({
someprop: { type: String }
})
) and this one causes already erros with 6.3.2: interface IWebUpload {
_id: string;
someprop: string;
}
function async a(): Promise<IWebUpload> {
const model = new MyModel({someprop: 'asdf'});
await model.save();
return model.toObject(); <-- errors
} |
I can look at this -- and will because I want to prevent regressions -- but part of the issue is that you're using what I would call an anti-pattern -- don't have your interface extend Document. Instead, do this: interface MyModel {
_id: string;
someprop: string;
}
type IDBMyModel = HydratedDocument<MyModel>;
const myModelSchema = new Schema<MyModel>({someProp: String});
const MyModel = model('MyModel', myModelSchema);\ You'll find your types all work better that way. ... that said, let me see if I can repro your issue in my PR branch |
@simllll also just to verify -- you realize your types are wrong for _id? The default type of _id is ObjectId, not string, so if you use that you're going to have inaccurate types unless you define _id as a string in your schema |
@simllll I see what is causing the issue, and it's a frustrating one because there isn't a good way to fix it, though there is a simple workaround. Here is the problem:
Because of this breaking change it perhaps should have gone into a 6.4 release instead of a patch release :-/ The problem is that while this has a minor but annoying workaround the problems caused by the other way don't have any workaround that I could discover. That said, if you just restructure so that you aren't extending Document (which is a really bad idea for lots of reasons anyway) then you won't have the issue and everything will Just Work (tm) =] |
Yeah I just was wirting the example directly inside the comment box, we actually use some more sophisticated typescript type we call "TypeObjectId<>" that is a generic so we can distinguish different object ids from each other. Regarding the extends Document, I will look into it right now and see if it's an easy fix. |
if not then just providing the type to the |
Still reviewing, but quick comment that this is related to #11118 |
For me I think I sorted it out by removing all the Hopefully all this effort has also some positive effects like typescript performance? 🫣👍 |
I went to sleep, woke up and saw the notifications and I thought: "Oh I hope the devs did not get frustrated that one and not the other PR was merged". First of all I thank you both, @taxilian and @mohammad0-0ahmad and others . I know that it could been frustrating but I think that you both had a respectful and valuable discussion why doing this or that. And yeah, it is kind of pedantic/neurological disorder to type complex code :). I have that too. I always try to make the typings strict as possible and correct as possible. People love and hate it 🗡️ Anyway. I think that we have to rethink the whole typings. I actually also dont understand why we have LeanDocument in the first place as it should be a POJO, which shape is based on the projection and if the lean flag or function was set. So I actually wonder if the following would make sense: The first thing is to get the automatic typing from mohammad0 getting properly reviewed and merged. I invite you, @taxilian to also review it. I will probably also review it and try and some test cases. But actually what should happen is, that when we create a query e.g. findOne, it should return Document if not lean option was set to true or lean() was called and it should return T, were T is in both cases the resulting type of the potential fields/projection setting. |
It's totally fine, it doesn't matter which one will be merged the important thing it to get the best possible result :) |
Agreed -- and while LeanDocument did originate with me I'm not so prideful that I can't see the limitations =] (I mean, it's a close thing, but still :-P) Which PR is the "automatic typings"? I'd love to review it. I really feel like there is an "ideal" solution out there but that we haven't found it yet, so maybe that's it ;-) I keep finding myself wondering if there is a way to make an alternate usage pattern for mongoose where you literally just pass around POJOs and then instead of "hydrating" documents you can either use them with "operate on this object" type functions or else with a backwards-compatibility style Proxy... but a) that is so far outside of this discussion that I should be shot for bringing it up and b) I'm well aware of just how much work that can end up being. Sorry for my impatience yesterday and thanks to all who contributed; I think the fix (mine) which was merged is the safest short term solution, but likely not the best long term solution so definitely want to keep involved with the discussions. I have two different jobs where I rely on all of this =] |
Do you want to request a feature or report a bug?
Report a bug
What is the current behavior?
I believe that the types defined here are incorrect:
mongoose/types/index.d.ts
Lines 2261 to 2267 in c22152c
This says that
LeanDocuments
(that is documents returned from.toObject()
or when using thelean
option don't have the_id
orid
fields. I don't think this is correct.This issue only presented itself in version 6.3.2 and wasn't present in 6.3.1
If the current behavior is a bug, please provide the steps to reproduce.
What is the expected behavior?
_id
andid
are present on LeanDocuments (I think...)What are the versions of Node.js, Mongoose and MongoDB you are using? Note that "latest" is not a version.
Node.js: v16.15.0
TypeScript: v4.6.2
mongoose: v6.3.2
The text was updated successfully, but these errors were encountered: