Skip to content
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

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

Closed
2 tasks done
iammola opened this issue Jul 12, 2022 · 1 comment
Closed
2 tasks done

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

iammola opened this issue Jul 12, 2022 · 1 comment
Labels
typescript Types or Types-test related issue / Pull Request
Milestone

Comments

@iammola
Copy link
Contributor

iammola commented Jul 12, 2022

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.

@IslandRhythms
Copy link
Collaborator

import * as mongoose from 'mongoose';

// 1. Create an interface representing a document in MongoDB.
interface IUser {
  name: string;
  email: string;
  avatar?: string;
}

// 2. Create a Schema corresponding to the document interface.
const userSchema = new mongoose.Schema<IUser>({
  name: { type: String, required: true },
  email: { type: String, required: true },
  avatar: String
});

// 3. Create a Model.
const User = mongoose.model<IUser>('User', userSchema);

run().catch(err => console.log(err));

async function run() {
  // 4. Connect to MongoDB
  await mongoose.connect('mongodb://localhost:27017/test');
  await mongoose.connection.dropDatabase();

  const user = new User({
    name: 'Bill',
    email: 'bill@initech.com',
    avatar: 'https://i.imgur.com/dM7Thhn.png'
  });
  await user.save();

  console.log(user.email); // 'bill@initech.com'
  User.exists({ name: 'Bill' }).then(doc => {
    if(doc != null) {
      const ID : mongoose.Types.ObjectId = doc._id;
    }
  });
}

@IslandRhythms IslandRhythms added the typescript Types or Types-test related issue / Pull Request label Jul 13, 2022
@vkarpov15 vkarpov15 added this to the 6.4.7 milestone Jul 17, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
typescript Types or Types-test related issue / Pull Request
Projects
None yet
Development

No branches or pull requests

3 participants