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

Using .select() doesn't update the type of a query's return value #13224

Closed
2 tasks done
Kio2047 opened this issue Mar 31, 2023 · 1 comment
Closed
2 tasks done

Using .select() doesn't update the type of a query's return value #13224

Kio2047 opened this issue Mar 31, 2023 · 1 comment
Labels
typescript Types or Types-test related issue / Pull Request
Milestone

Comments

@Kio2047
Copy link

Kio2047 commented Mar 31, 2023

Prerequisites

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

Mongoose version

7.0.1

Node.js version

18.12.1

MongoDB server version

5.0.15

Typescript version (if applicable)

5.0.2

Description

When using chaining .select() onto a findOne query, TypeScript doesn't recognise that only the specified fields will be present on the retrieved document, and subsequently doesn't complain if one tries to access a field that was omitted in the .select(), leaving us to rely on type casting or other measures which would otherwise be unnecessary.

Steps to Reproduce

import mongoose from 'mongoose'

interface UserI{
  email: string;
  passwordHash: string;
  name: string;
  username: string;
  profileImageURL: string;
}

const userSchema = new mongoose.Schema<UserI>({
  email: { type: String, required: true, unique: true, index: true },
  passwordHash: { type: String, required: true },
  name: { type: String, required: true, index: true },
  username: { type: String, required: true, unique: true, index: true },
  profileImageURL: { type: String, required: true },
})

const User = mongoose.model<UserI>("User", userSchema);

const getUserByID = async function (id: string) {
  const account = await User.findById(id).select("username");
  if (!account) return;
  // TypeScript thinks the all user properties are present, within the retrieved document, and doesn't complain on the line below
  account.profileImageURL
  return account;
};

Expected Behavior

At the moment I'm using the following as a work-around - I'll leave it here just in case it's helpful:

type Nullable<T> = T | null;

type FindOneResult<
  Document,
  Fields extends keyof Document = keyof Document
> = Nullable<Pick<HydratedDocument<Document>, Fields>>;

type FindOnePromise<
  Document,
  Fields extends keyof Document = keyof Document
> = Promise<FindOneResult<Document, Fields>>;

export const getUserByID = async <
  Fields extends Exclude<keyof RawUserDocument, "passwordHash"> = Exclude<
    keyof RawUserDocument,
    "passwordHash"
  >
>(
  id: string,
  options?: {
    fields: Fields[];
  }
): FindOnePromise<RawUserDocument, Fields> => {
  let account: FindOneResult<RawUserDocument, Fields>;
  if (options?.fields) {
    account = await User.findById(id).select(options.fields.join(" "));
  } else {
    account = await User.findById(id).select("-passwordHash");
  }
  return account;

This requires desired fields to be submitted in an array. I'm not sure how you'd go about this if the fields were submitted using a string in which fields are space separated, as is the case with mongoose's select.

@vkarpov15
Copy link
Collaborator

We'll consider adding this to 7.1, something like .select<{ username: string }>('username').

@vkarpov15 vkarpov15 added this to the 7.1.0 milestone Apr 4, 2023
@vkarpov15 vkarpov15 added the typescript Types or Types-test related issue / Pull Request label Apr 4, 2023
@vkarpov15 vkarpov15 modified the milestones: 7.1.0, 7.2.0 Apr 19, 2023
@vkarpov15 vkarpov15 modified the milestones: 7.2.0, 7.3.0 May 7, 2023
vkarpov15 added a commit that referenced this issue Jun 4, 2023
vkarpov15 added a commit that referenced this issue Jun 7, 2023
types: allow overwriting `Paths` in `select()` to tell TypeScript which fields are projected
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

2 participants