If you know how to fix the issue, make a pull request instead.
@types/mongoose package and had problems.Definitions by: in index.d.ts) so they can respond.The lean method should not return an _Object_. The current behavior breaks types and forces the user to do type checking. See https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/mongoose/index.d.ts#L1582
Idee: Maybe enable the Model to specify a interface which is then sub-sequentially returned in the Query in the lean method?
@floriantraber
馃槼 sorry I missed this. We can probably add something like lean<U>(bool?: boolean): Query<U>;
and use it like this:
...
.lean<MyType>().exec(function(err, result) {
// typeof result is MyType
});
Is this what you're looking for? Could I get an example of some code that breaks for you?
I like the idea and it's better than currently.
The following is an example, where one can call the save method on the user and not on the leanUser., but both have a property name. This should be reflected in the types.
It think it would be even better, if it was not necessary to specify the type every time. It's unnecessary and error prone (Losing of the information if the results is an object or array).
export type User = { _id: any; name: string; };
export type UserDocument = mongoose.Document & User;
export const userSchema = new mongoose.Schema({ name: String });
export const userModel = mongoose.model<UserDocument>('User', userSchema);
const user: UserDocument = await userModel.findOne();
// currently
const leanUser: User = await userModel.findOne().lean() as User;
// proposed
const leanUser: User = await userModel.findOne().lean<User>();
// ideal
const leanUser: User = await userModel.findOne().lean();
These are great examples, I'll look into it.
@floriantraber Unfortunately, I don't think we have any way of extracting the User interface out of UserDocument.
However, if we do this:
lean(bool?: boolean): Query<any>;
Then this will work:
const leanUser: User = await userModel.findOne().lean();
Does that work?
I would prefer the _generic_ version from your first post to the _any_ version as it's giving more explicit type safety.
with any:
const leanUser: User = await userModel.findOne().lean();
const leanUser = await userModel.findOne().lean(); // leanUser is any
with generic:
const leanUser: User = await userModel.findOne().lean<User>();
const leanUser: User = await userModel.findOne().lean(); // leanUser is {}, ts error
Would it be possible to add the User interface as an additional, optional type parameter at model creation? (at mongoose.model<T>()) This would supply us with the needed type information when calling the lean method.
We can't add optional type parameters at model creation because there is already a type parameter mongoose.model<T extends Document>(). Removing this would cause a lot of breaking changes.
The best we can do here is:
const leanUser = await userModel.findOne().lean<User>();
// or
const leanUser: User = await userModel.findOne().lean();
If it's not possible to add an additional parameter at model creation, I would prefer the generic variant as noted above.
const leanUser = await userModel.findOne().lean<User>();
@floriantraber See here: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/21504#discussion_r152714680
Unfortunately I'm told that we should prefer using:
lean() as User instead of lean<User>(): User
because the type assertion is more explicit.
See the getMeAT<T>(): T bullet point in this section of the DefinitelyTyped readme:
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/715a90d/README.md#common-mistakes
If I were writing this from scratch, I'd change:
interface Model<T extends Document> extends NodeJS.EventEmitter, ModelProperties {
To:
interface Model<P, T=P & Document> extends NodeJS.EventEmitter, ModelProperties {
Then you'd have the Document type T, and the "POJO" type P (and you wouldn't have to specify T explicitly unless you add members to it).
Then when you do new User(doc), doc would be a P instead of an any, and lean() would return a Query<P>, and everything would be wonderful. :P
You could even make this backwards compatible:
interface ModelWithPojo<P, T=P & Document> extends NodeJS.EventEmitter, ModelProperties {
...
}
export type Model<T> = ModelWithPojo<object, T>;
I'm happy to take a crack at a PR? I'm still on mongoose 4 though. :P
quickly tested with bit hacking, but I didn't manage to get it actually working
interface ModelWithPojo<P extends object, QueryHelpers = {}, T = P & Document> extends NodeJS.EventEmitter, ModelProperties {
... this fails as "type 'T' does not satisfy the constraint 'Document'" on multiple functions.
It definitely would be nice to be more strict with types like for new () and .lean()
Most helpful comment
If I were writing this from scratch, I'd change:
To:
Then you'd have the
DocumenttypeT, and the "POJO" typeP(and you wouldn't have to specify T explicitly unless you add members to it).Then when you do
new User(doc), doc would be aPinstead of anany, andlean()would return aQuery<P>, and everything would be wonderful. :PYou could even make this backwards compatible:
I'm happy to take a crack at a PR? I'm still on mongoose 4 though. :P