Skip to content

Model.exists result _id is the document's type #12094

@iammola

Description

@iammola

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Mongoose version

6.4.3

Node.js version

18.4.0

MongoDB server version

5.0

Description

Using Model.exists to check if a document exists with the specified query is meant to return the Document's _id if it exists and null otherwise.

But I get a TS error when accessing the _id because it's not the expected value type.

When I read the types to understand the error in these lines,

mongoose/types/models.d.ts

Lines 308 to 313 in db1ba6e

/**
* Returns a document with its `_id` if at least one document exists in the database that matches
* the given `filter`, and `null` otherwise.
*/
exists(filter: FilterQuery<T>, callback: Callback<Pick<Document<T>, '_id'> | null>): QueryWithHelpers<Pick<Document<T>, '_id'> | null, HydratedDocument<T, TMethodsAndOverrides, TVirtuals>, TQueryHelpers, T>;
exists(filter: FilterQuery<T>): QueryWithHelpers<Pick<Document<T>, '_id'> | null, HydratedDocument<T, TMethodsAndOverrides, TVirtuals>, TQueryHelpers, T>;

The first parameter in the Document class is expected to be _id's type. But the T variable that's passed is the Schema's resolved type.
/**
* Generic types for Document:
* * T - the type of _id
* * TQueryHelpers - Object with any helpers that should be mixed into the Query type
* * DocType - the type of the actual Document created
*/
class Document<T = any, TQueryHelpers = any, DocType = any> {
constructor(doc?: any);
/** This documents _id. */
_id?: T;


From the documatation,

Under the hood, MyModel.exists({ answer: 42 }) is equivalent to MyModel.findOne({ answer: 42 }).select({ _id: 1 }).lean()

So the RequireOnlyTypedId type could be better to use as the Model.exists return type because the _id property always exists and the returned document is always a POJO unless it's not found then null.

mongoose/types/index.d.ts

Lines 117 to 119 in db1ba6e

export type RequireOnlyTypedId<T> = T extends { _id?: infer U; }
? Required<{ _id: U }>
: { _id: Types.ObjectId };

Then the method definition would be something like,

exists(filter: FilterQuery<T>, callback: Callback<RequireOnlyTypedId<T> | null>): QueryWithHelpers<RequireOnlyTypedId<T> | null, HydratedDocument<T, TMethodsAndOverrides, TVirtuals>, TQueryHelpers, T>; 
exists(filter: FilterQuery<T>): QueryWithHelpers<RequireOnlyTypedId<T> | null, HydratedDocument<T, TMethodsAndOverrides, TVirtuals>, TQueryHelpers, T>; 

Steps to Reproduce

const schema = new mongoose.Schema({ name: String });
const Model = mongoose.model('Model', schema);

Model.exists({ name: "Hey!" }).then(doc => {
  if (doc != null) {
    const ID: mongoose.Types.ObjectId = doc._id;
    // Type '{ name?: string | undefined; } | undefined' is not assignable to type 'ObjectId'.
    // Type 'undefined' is not assignable to type 'ObjectId'.ts(2322)
  }
});

Expected Behavior

For the _id property to be the Document's _id type not the entire document type.

Metadata

Metadata

Assignees

No one assigned

    Labels

    typescriptTypes or Types-test related issue / Pull Request

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions