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

Subdocument type is broken? #15211

Open
2 tasks done
Clockworkx opened this issue Jan 28, 2025 · 3 comments · May be fixed by #15240
Open
2 tasks done

Subdocument type is broken? #15211

Clockworkx opened this issue Jan 28, 2025 · 3 comments · May be fixed by #15240
Labels
docs This issue is due to a mistake or omission in the mongoosejs.com documentation
Milestone

Comments

@Clockworkx
Copy link

Prerequisites

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

Mongoose version

8.9.5

Node.js version

22.4.0

MongoDB server version

ts issue

Typescript version (if applicable)

5.3.2

Description

Dear Mongoose Team,
When providing an override for Subdocuments, access to the properties of that subdocument is gone.
I took the example from the docs in the typescript/subdocuments section as a reproduceable example.
Hopefully it's not me being a dummy.

Thanks.

Steps to Reproduce

// Setup
import { Schema, Types, model, Model } from "mongoose";

// Subdocument definition
interface Names {
  _id: Types.ObjectId;
  firstName: string;
}

// Document definition
interface User {
  names: Names;
}

type THydratedUserDocument = {
  names?: Types.Subdocument<Names>;
};

// Models and schemas
type UserModelType = Model<User, {}, {}, {}, THydratedUserDocument>;

const userSchema = new Schema<User, UserModelType>({
  names: new Schema<Names>({ firstName: String }),
});
const UserModel = model<User, UserModelType>("User", userSchema);

// Create a new document:
const doc = new UserModel({ names: { _id: "0".repeat(24), firstName: "foo" } });

doc.names!.ownerDocument(); // Works, `names` is a subdocument!
doc.names.firstName; // Property firstName does not exist on type 

Expected Behavior

I expect to be able to access .firstName on .names.

@nikzanda
Copy link

nikzanda commented Jan 29, 2025

Hi, try with this code:

// Setup
import { Schema, Types, model, Model, HydratedSingleSubdocument, HydratedDocument, SchemaTypes } from "mongoose";

// Subdocument definition
interface INames {
  _id: Types.ObjectId;
  firstName: string;
}

interface INamesVirtuals {
  id: string;
}

type NamesInstance = HydratedSingleSubdocument<INames, INamesVirtuals>;

type NamesModelType = Model<INames, {}, {}, INamesVirtuals, NamesInstance>

const namesSchema = new Schema<INames, NamesModelType>({
  firstName: {
    type: SchemaTypes.String,
    required: true,
  }
})

// Document definition
interface IUser {
  names: INames;
}

type UserDocumentOverrides = {
  names: NamesInstance;
};

interface IUserVirtuals {
  id: string;
}

type UserInstance = HydratedDocument<IUser, UserDocumentOverrides & IUserVirtuals>

// Models and schemas
type UserModelType = Model<IUser, {}, UserDocumentOverrides, IUserVirtuals, UserInstance>;

const userSchema = new Schema<IUser, UserModelType>({
  names: {
    type: namesSchema,
    required: true,
  },
});
const UserModel = model<IUser, UserModelType>("User", userSchema);

// Create a new document:
const doc = new UserModel({ names: { _id: "0".repeat(24), firstName: "foo" } });

doc.names.ownerDocument();
doc.names.firstName;

@Clockworkx
Copy link
Author

Clockworkx commented Jan 29, 2025

Thanks a lot for your suggestion @nikzanda .
I am however a bit sceptical. Model's 5th parameter (THydratedDocumentType - mongoose code) is the output type for findOne, new... We provide it for both subdocument arrays (Document arrays) and single Subdocuments in the doc examples.
DocumentArray, when not providing a 2nd generic param (what the docs do), provides a default that provides the DocType to Subdocument and ands with T.

class DocumentArray<T, THydratedDocumentType extends Types.Subdocument<any> = Types.Subdocument<InferId<T>, any, T> & T> extends Types.Array<THydratedDocumentType> {

Subdocument however does not do that.

class Subdocument<IdType = unknown, TQueryHelpers = any, DocType = any> extends Document<IdType, TQueryHelpers, DocType> 

Here, what the docs suggest is Subdocument<Names>, so Name is IdType and there is no & with Names, or providing DocType.

Based on my understanding it does not make sense that the properties are discarded as in the docs. That seems like a bug instead.

Also, having to provide a HydratedDocument or HydratedSingleSubdocument to Model's THydratedDocumentType does not make sense to me, Model should take care of that, no? Output of find, new.. is always hydrated, unless with lean, right?

@vkarpov15 vkarpov15 added the typescript Types or Types-test related issue / Pull Request label Jan 30, 2025
@vkarpov15 vkarpov15 modified the milestones: 8.9.7, 8.10.1 Jan 30, 2025
@vkarpov15
Copy link
Collaborator

Try the following approach:

// Setup
import { Schema, Types, model, Model, HydratedSingleSubdocument } from "mongoose";

// Subdocument definition
interface Names {
  _id: Types.ObjectId;
  firstName: string;
}

// Document definition
interface User {
  names: Names;
}

type THydratedUserDocument = {
  names?: HydratedSingleSubdocument<Names>;
};

// Models and schemas
type UserModelType = Model<User, {}, {}, {}, THydratedUserDocument>;

const userSchema = new Schema<User, UserModelType>({
  names: new Schema<Names>({ firstName: String }),
});
const UserModel = model<User, UserModelType>("User", userSchema);

// Create a new document:
const doc = new UserModel({ names: { _id: "0".repeat(24), firstName: "foo" } });

doc.names!.ownerDocument();
doc.names!.firstName;

HydratedSingleSubdocument is the way to go for defining a type that is a single subdoc with properties. Types.Subdocument<> doesn't add on any properties. We need to update our docs to fix this.

@vkarpov15 vkarpov15 added docs This issue is due to a mistake or omission in the mongoosejs.com documentation and removed typescript Types or Types-test related issue / Pull Request labels Feb 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs This issue is due to a mistake or omission in the mongoosejs.com documentation
Projects
None yet
3 participants