Definitelytyped: mongoose should better support lean

Created on 23 Oct 2017  路  10Comments  路  Source: DefinitelyTyped/DefinitelyTyped

If you know how to fix the issue, make a pull request instead.

  • [X] I tried using the @types/mongoose package and had problems.
  • [X] I tried using the latest stable version of tsc. https://www.npmjs.com/package/typescript
  • [X] I have a question that is inappropriate for StackOverflow. (Please ask any appropriate questions there).
  • [X] [Mention](https://github.com/blog/821-mention-somebody-they-re-notified) the authors (see Definitions by: in index.d.ts) so they can respond.

    • Authors: @simonxca @horiuchi @sindrenm

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?

Most helpful comment

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

All 10 comments

@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()

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lilling picture lilling  路  3Comments

ArtemZag picture ArtemZag  路  3Comments

victor-guoyu picture victor-guoyu  路  3Comments

Zzzen picture Zzzen  路  3Comments

csharpner picture csharpner  路  3Comments