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

InferSchemaType does not infer types for virtual fields from schema #12684

Open
2 tasks done
Jokero opened this issue Nov 11, 2022 · 5 comments · Fixed by #12860
Open
2 tasks done

InferSchemaType does not infer types for virtual fields from schema #12684

Jokero opened this issue Nov 11, 2022 · 5 comments · Fixed by #12860
Labels
typescript Types or Types-test related issue / Pull Request
Milestone

Comments

@Jokero
Copy link
Contributor

Jokero commented Nov 11, 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.7.2

Node.js version

18.12.0

MongoDB server version

N/A

Typescript version (if applicable)

4.8.4

Description

InferSchemaType does not infer types for virtual fields when schema has any. According to this PR #11908, it should work.

Steps to Reproduce

  1. Copy example from mentioned PR https://github.com/Automattic/mongoose/pull/11908/files#diff-fc5a8155d58dc5904b33fcc871f11ee08cb6f6e7adbe69e7bfbd7d173e82d8d2 (Automatically Inferred Types), create a type with InferSchemaType and try to access fullName:
import { Schema, InferSchemaType } from 'mongoose';

const schema = new Schema(
    {
        firstName: String,
        lastName: String,
    },
    {
        virtuals:{
            fullName:{
                get(){
                    return `${this.firstName} ${this.lastName}`;
                }
                // virtual setter and options can be defined here as well.
            }
        }
    }
);

type User = InferSchemaType<typeof schema>;

const user: User = {};
user.fullName
  1. You will get the following error:
TS2339: Property 'fullName' does not exist on type '{ firstName?: string | undefined; lastName?: string | undefined; }'.

Expected Behavior

Type User should contain fullName: string field, and user.fullName shouldn't fail TS compilation.

@IslandRhythms IslandRhythms added the typescript Types or Types-test related issue / Pull Request label Nov 16, 2022
@carlosingles
Copy link

As a work around, you can use the ObtainSchemaGeneric type to add the virtual types:

import { Schema, InferSchemaType, ObtainSchemaGeneric } from 'mongoose';
...
type User = InferSchemaType<typeof schema> & ObtainSchemaGeneric<typeof schema, 'TVirtuals'>;

const user = {} as User;
user.fullName // works

@vkarpov15 vkarpov15 added this to the 6.7.4 milestone Nov 28, 2022
@Jokero
Copy link
Contributor Author

Jokero commented Nov 28, 2022

@carlosingles, thanks for the workaround, currently I hardcoded all virtuals in an interface.

@vkarpov15, I've tested the example from my initial message in 6.7.4, and the issue still persists (fullName is not available in a type returned byInferSchemaType).

If speak generally, I think it makes sense to have a way to get both interfaces (with and without virtuals). Based on first message example:

  1. full interface with virtuals which can be used with find queries
type FullUser = InferFullSchemaType<typeof schema>;

//{
//  _id: ObjectId;
//  id: string;
//  firstName: string;
//  lastName: string;
//  fullName: string;
//}

function findOneUser(): Promise<FullUser> {
  return UserModel.findOne().orFail().exec();
}
  1. persistent interface (only with persisted fields and without _id) that can be used with creation method
type PersistentUserData = InferPersistentSchemaType<typeof schema>;

//{
//  firstName: string;
//  lastName: string;
//}

function createUser(data: PersistentUserData): Promise<FullUser> {
  return UserModel.create(data);
}

Theoretically _id field can be omitted manually, but this won't work if there are nested fields with _id.

@vkarpov15 vkarpov15 reopened this Dec 2, 2022
@vkarpov15 vkarpov15 modified the milestones: 6.7.4, 6.7.6 Dec 2, 2022
@vkarpov15 vkarpov15 modified the milestones: 6.8.1, 6.8.3 Dec 16, 2022
@vkarpov15
Copy link
Collaborator

@Jokero I took a look and that's expected behavior. InferSchemaType returns the raw document interface, which represents how the data is stored in MongoDB. The Model does correctly contain the virtuals, try the following:

const UserModel = model('AutoTypedVirtuals', schema);
type User = ReturnType<(typeof UserModel)['hydrate']>;

const user: User = new UserModel({ firstName: 'foo' });
user.fullName;

@vkarpov15
Copy link
Collaborator

@Jokero are you using mongoose-lean-virtuals? lean() doesn't include virtuals by default.

vkarpov15 added a commit that referenced this issue Jan 6, 2023
docs(typescript): explain that virtuals inferred from schema only show up on Model, not raw document type
@vkarpov15 vkarpov15 reopened this Jan 6, 2023
@vkarpov15 vkarpov15 modified the milestones: 6.8.3, 6.8.5 Jan 6, 2023
@Jokero
Copy link
Contributor Author

Jokero commented Jan 6, 2023

@vkarpov15 yes, I'm using it like this lean({ autopopulate: true, virtuals: true }). In some cases I also specify the type explicitly lean<Quest | null>({ autopopulate: true, virtuals: true })

@vkarpov15 vkarpov15 reopened this Jan 14, 2023
@vkarpov15 vkarpov15 modified the milestones: 6.8.5, 6.8.6 Jan 23, 2023
@vkarpov15 vkarpov15 modified the milestones: 6.9.1, 6.9.2, TypeScript backlog Feb 3, 2023
@vkarpov15 vkarpov15 removed this from the TypeScript backlog milestone Dec 16, 2024
@vkarpov15 vkarpov15 modified the milestones: 5.13.23, 8.9.2, 8.9.3 Dec 16, 2024
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
4 participants