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

Type instantiation is excessively deep and possibly infinite. #10617

Closed
simllll opened this issue Aug 26, 2021 · 33 comments
Closed

Type instantiation is excessively deep and possibly infinite. #10617

simllll opened this issue Aug 26, 2021 · 33 comments
Labels
typescript Types or Types-test related issue / Pull Request
Milestone

Comments

@simllll
Copy link
Contributor

simllll commented Aug 26, 2021

Do you want to request a feature or report a bug?*
bug

What is the current behavior?
It seems if a model defintion has a property with type "any", all calls to findOne etc have typescript errors with
error TS2589: Type instantiation is excessively deep and possibly infinite.

If the current behavior is a bug, please provide the steps to reproduce.

interface IDBModel extends Document { 
        date: Date; // date created
	_tags: any[];
}

const Schema = new Schema({
date: { type: Date, default: Date.now }, // date created
_tags: [{ type: Schema.Types.ObjectId, ref: 'Tag' }]
});

const DBModel: Model<IDBModel> = model<IDBModel>('Meep', Schema);

DBModel.findOne({date: new Date()})... // error

What is the expected behavior?
should not throw the TS error.

As a workaround, setting it to "unknown" works

What are the versions of Node.js, Mongoose and MongoDB you are using? Note that "latest" is not a version.
mongoose 6.0.0

@vkarpov15 vkarpov15 added this to the 6.0.2 milestone Aug 26, 2021
@vkarpov15 vkarpov15 added the typescript Types or Types-test related issue / Pull Request label Aug 26, 2021
@vkarpov15
Copy link
Collaborator

@simllll below script compiles fine on my machine with Mongoose 6.0.0 and 6.0.1, no errors. Can you please clarify your TypeScript version and tsconfig, and modify the below script to demonstrate your issue?

import { connect, model, Schema, Model } from 'mongoose';
  
run().catch(err => console.log(err));

async function run() {
  await connect('mongodb://localhost:27017/test');

  interface IDBModel extends Document {
        date: Date; // date created
        _tags: any[];
  }

  const schema = new Schema({
    date: { type: Date, default: Date.now }, // date created
    _tags: [{ type: Schema.Types.ObjectId, ref: 'Tag' }]
  });

  const DBModel: Model<IDBModel> = model<IDBModel>('Meep', schema);

  DBModel.findOne({date: new Date()})
}

@vkarpov15 vkarpov15 removed this from the 6.0.2 milestone Aug 26, 2021
@vkarpov15 vkarpov15 added can't reproduce Mongoose devs have been unable to reproduce this issue. Close after 14 days of inactivity. and removed typescript Types or Types-test related issue / Pull Request labels Aug 26, 2021
@Yoink3000
Copy link

i get this error when i try to use SomeModel.find({})

@vkarpov15
Copy link
Collaborator

@LOLBRUHNICE please provide a repro script or at least some code samples

@rongem
Copy link

rongem commented Aug 26, 2021

I also get this error after updating from mongoose@5.13.7 to mongoose@5.13.8. It also occurs when updating to 6.0.1.
@types/node is LTS, the rest of the packages are up to date.

It occurs when you have an array of references:

const itemTypeSchema = new Schema<IItemType, IItemTypeModel>({
  // removed other attributes that cause no error here
  attributeGroups: [{ // this causes the error
    type: Types.ObjectId,
    required: true,
    ref: 'AttributeGroup',
  }],
});

The error occurs only when extending Document for IItemType. If not extending it, there is no _id property, which makes filtering an other operations impossible.

Also I tried and failed migration to version 6. There is documentation missing that explains how to correctly use PopulatedDoc with Typescript in complex situations.

Only workaround seems to be using strings instead of Types.ObjectId.

@vkarpov15 vkarpov15 added this to the 6.0.3 milestone Aug 26, 2021
@vkarpov15 vkarpov15 added needs repro script Maybe a bug, but no repro script. The issue reporter should create a script that demos the issue and removed can't reproduce Mongoose devs have been unable to reproduce this issue. Close after 14 days of inactivity. labels Aug 26, 2021
@Yoink3000
Copy link

Yoink3000 commented Aug 27, 2021

interface XXX extends mongoose.Document {
	SomeProperty: any[]
}

@Yoink3000
Copy link

interface XXX extends mongoose.Document {
	SomeProperty: any[]
}

it happen once i upgrade to 6.x.x

@simllll
Copy link
Contributor Author

simllll commented Aug 27, 2021

@vkarpov15 I tried to create a reproducable example, and stumpled upon something very strange.
My script:
test.ts

import { Document, Model, model, Schema } from 'mongoose';

interface IDBModel extends Document {
	date: Date; // date created
	_tags: any[];
}

const schema = new Schema({
	date: { type: Date, default: Date.now }, // date created
	_tags: [{ type: Schema.Types.ObjectId, ref: 'Tag' }]
});

export const DBModel: Model<IDBModel> = model<IDBModel>('Meep', schema);

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

async function run() {
	await DBModel.findOne({
		[`internal.trackingPingBacks.action`]: 'asdf'
	});
}

is basically the same as yours, just minor modifications.
First I ran it with ts-node .. no compile issues
2nd "tsc test.ts" ... no complie issues
and now just "tsc" .. without any arguments, compile ERRO!: test.ts:19:8 - error TS2589: Type instantiation is excessively deep and possibly infinite.

Not quite sure if this is a typescript bug, or what exactly is going on here.
image

Do you have any idea or should I open a ticket at typescript?

tsc --version: 4.3.5
Update: also happening with latest tsc (4.4.2)

@Yoink3000
Copy link

Yoink3000 commented Aug 27, 2021

I found that we can replace
any[]
to
any
to solve the problem

@vkarpov15 vkarpov15 added has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue and removed needs repro script Maybe a bug, but no repro script. The issue reporter should create a script that demos the issue labels Aug 27, 2021
@vkarpov15 vkarpov15 modified the milestones: 6.0.3, 6.0.4 Aug 30, 2021
@vkarpov15
Copy link
Collaborator

@simllll I took a closer look and this looks to be caused by our UpdateQuery type. We're investigating, but haven't found a good fix yet.

One way you can fix this issue is to remove the extends Document. We recommend your document interfaces not extend Document, here's the docs on that: https://mongoosejs.com/docs/typescript.html#using-extends-document .

@vkarpov15 vkarpov15 added typescript Types or Types-test related issue / Pull Request and removed has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue labels Sep 1, 2021
@rongem
Copy link

rongem commented Sep 2, 2021

@vkarpov15: This hint does not solve the problem, but moves it to another place. What is missing is documentation that explains how to use sub documents and references in TypeScript without using the extends document. Imagine an array of subdocuments in a document and then using a query filter looking for special ObjectIds inside the sub documents.
Removing the extends document leaves me with a completely broken application and no idea how to fix this.

@vkarpov15
Copy link
Collaborator

@rongem can you please provide code samples that illustrate the issue you're seeing? What sort of special ObjectIds?

Also, if you upgrade to v6.0.4 this compilation issue should be fixed, even if you're using extends Document.

@vkarpov15 vkarpov15 removed the can't reproduce Mongoose devs have been unable to reproduce this issue. Close after 14 days of inactivity. label Oct 2, 2021
@vkarpov15 vkarpov15 modified the milestones: 6.0.13, 6.0.10 Oct 2, 2021
@vkarpov15
Copy link
Collaborator

@rongem still no luck, below script compiles fine on both Mongoose 6.0.9 and Mongoose 6.0.1

import { Document, Model, model, Schema, Types } from 'mongoose';
  
export interface IAttributeGroup extends Document {
  name: string;
}

const attributeGroupSchema = new Schema<IAttributeGroup, IAttributeGroupModel, IAttributeGroup>({
  name: {
    type: String,
    required: true,
    unique: true,
  },
});

attributeGroupSchema.statics.validateIdExists = (value: Types.ObjectId) => attributeGroupModel.findById(value).countDocuments()
  .then((docs: number) => Promise.resolve(docs > 0))
  .catch((error: any) => Promise.reject(error));

attributeGroupSchema.pre('find', function() { this.sort('name'); });

export interface IAttributeGroupModel extends Model<IAttributeGroup> {
  validateIdExists(value: Types.ObjectId): Promise<boolean>;
}

export const attributeGroupModel = model<IAttributeGroup, IAttributeGroupModel>('AttributeGroup', attributeGroupSchema);


export async function itemTypeModelCreate(name: string) {
  let itemType = await attributeGroupModel.create({ name });
}

Can you please create a TypeScript playground example that demonstrates the issue you're seeing @skhilliard or @rongem ? I haven't been able to repro this issue.

@vkarpov15 vkarpov15 added the can't reproduce Mongoose devs have been unable to reproduce this issue. Close after 14 days of inactivity. label Oct 6, 2021
@vkarpov15 vkarpov15 removed this from the 6.0.10 milestone Oct 6, 2021
@rongem
Copy link

rongem commented Oct 6, 2021

@vkarpov15: I am an old man and no longer seen on playgrounds. :)
But I zipped a minimum code example that throws the error:
test.zip

If you want the full example (with more errors of that kind), you may use the njs-backend project on https://github.com/rongem/cmdb

@vkarpov15 vkarpov15 added this to the 6.0.11 milestone Oct 8, 2021
@vkarpov15 vkarpov15 added has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue and removed can't reproduce Mongoose devs have been unable to reproduce this issue. Close after 14 days of inactivity. labels Oct 8, 2021
vkarpov15 added a commit that referenced this issue Oct 14, 2021
…inition to avoid "excessively deep and possibly infinite" TS errors

Fix #10617
@vkarpov15
Copy link
Collaborator

@rongem we backported some fixes to 5.x that look like they fix your issue. These will be released in 5.3.12.

Is there anything preventing you from upgrading to Mongoose 6? These issues are fixed in Mongoose 6.0.10.

@vkarpov15 vkarpov15 modified the milestones: 6.0.11, 5.13.12 Oct 14, 2021
@vkarpov15 vkarpov15 removed the has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue label Oct 14, 2021
@rongem
Copy link

rongem commented Oct 15, 2021

@vkarpov15: I tried to port it already, but failed. The migration documentation seems incomplete. Handling of strings where BSON is used in MongoDB seems to be different.
Since I am in the process of redesigning the frontend at the moment, it would be a big bunch of extra work. So sticking to v5 at the moment is warmly appreciated.

@vkarpov15
Copy link
Collaborator

"Handling of strings where BSON is used in MongoDB seems to be different." <-- any more info on this?

@rongem
Copy link

rongem commented Oct 19, 2021

In version 5, there was no problem using strings for object definitions when using ObjectIds in schema. Mongoose did all the heavy lifting.

I created a branch where you can see the problem:

https://github.com/rongem/cmdb/tree/mongoose6

If you open folder njs-backend in VS Code and then navigate to /src/models/mongoose/historic-connection.model.ts, you'll see the mess after the upgrade. I have no idea in which direction I will have to fix this, and there is no mongoose/typescript documentation that helps. What is there ends a few millimeters below the surface.

@rongem
Copy link

rongem commented Oct 22, 2021

@vkarpov15: I can confirm that the initial error "Type instantiation is excessively deep and possibly infinite" is resolved in 5.13.12. Thank you for that.

Nevertheless, all the new type restrictions from Mongoose 6 now also apply, so it seems I have to fix them anyway. Sigh.

Type 'string' is not assignable to type 'Condition<PopulatedDoc<IConfigurationItem, ObjectId>> | undefined'
No overload matches this call.
  Overload 1 of 3, '(callback?: Callback<IConnection[]> | undefined): Query<IConnection[], IConnection, {}, IConnection>', gave the following error.
    Argument of type '{ $and: ({ $or: ({ upperItem: { $in: any[]; }; } | { lowerItem: { $in: any[]; }; })[]; } | { connectionRule: { $in: string[]; }; })[]; }' is not assignable to parameter of type 'Callback<IConnection[]>'.
      Object literal may only specify known properties, and '$and' does not exist in type 'Callback<IConnection[]>'.
  Overload 2 of 3, '(filter: FilterQuery<IConnection>, callback?: Callback<IConnection[]> | undefined): Query<IConnection[], IConnection, {}, IConnection>', gave the following error.
    Type 'string[]' is not assignable to type 'PopulatedDoc<IConnectionRule, ObjectId>[]'.
      Type 'string' is not assignable to type 'PopulatedDoc<IConnectionRule, ObjectId>'.
  Overload 3 of 3, '(filter: FilterQuery<IConnection>, projection?: any, options?: QueryOptions | null | undefined, callback?: Callback<IConnection[]> | undefined): Query<...>', gave the following error.      
    Type 'string[]' is not assignable to type 'PopulatedDoc<IConnectionRule, ObjectId>[]'.

@rongem
Copy link

rongem commented Oct 22, 2021

To make the newly introduced problems clearer:
{$or: [{lowerItem: req.params[idField])}, {upperItem: req.params[idField]}]}
for a query fails, because of the strings being used in the request ojbect. The code has to become
{$or: [{lowerItem: new Types.ObjectId(req.params[idField])}, {upperItem: new Types.ObjectId(req.params[idField])}]}
in version 5 > 5.13.7 to work.

After fixing all breaking changes to v 5, I made up a new branch for mongoose6: https://github.com/rongem/cmdb/tree/mongoose6.

@rongem
Copy link

rongem commented Oct 22, 2021

@vkarpov15 After running trough the migration guide and changing everything that affects me, I still have type errors. To make clear where the documentation has flaws, I show you two examples. The documentation under https://mongoosejs.com/docs/typescript/schemas.html only contains text fields, no SubDocuments nor ObjectIds. I tried two variants, and both throw compilation errors of the same type when changing vom v5 to v6:

1.) Working with PopulatedDoc

export interface IConnection extends Document {
    connectionRule: PopulatedDoc<IConnectionRule, Types.ObjectId>;
    upperItem: PopulatedDoc<IConfigurationItem, Types.ObjectId>;
    lowerItem: PopulatedDoc<IConfigurationItem, Types.ObjectId>;
    description: string;
}

const connectionSchema = new Schema<IConnection, Model<IConnection>>({
    connectionRule: {
        type: Types.ObjectId,
        required: true,
        index: true,
        ref: 'ConnectionRule',
    },
    upperItem: {
        type: Types.ObjectId,
        required: true,
        index: true,
        ref: 'ConfigurationItem',
    },
    lowerItem: {
        type: Types.ObjectId,
        required: true,
        index: true,
        ref: 'ConfigurationItem',
    },
    description: {
        type: String,
        required: false,
        default: '',
    }
});

2.) Working with strings only

export interface IHistoricConnection extends Document, SchemaTimestampsConfig {
    connectionRuleId: string;
    connectionTypeId: string;
    connectionTypeName: string;
    connectionTypeReverseName: string;
    upperItemId: string;
    lowerItemId: string;
    descriptions: Types.Array<string>;
    deleted: boolean;
}

const historicConnectionSchema = new Schema<IHistoricConnection,  Model<IHistoricConnection>>({
    connectionRuleId: {
        type: Types.ObjectId,
        required: true,
    },
    connectionTypeId: {
        type: Types.ObjectId,
        required: true,
    },
    connectionTypeName: {
        type: String,
        required: true,
    },
    connectionTypeReverseName: {
        type: String,
        required: true,
    },
    upperItemId: {
        type: Types.ObjectId,
        required: true,
    },
    lowerItemId: {
        type: Types.ObjectId,
        required: true,
    },
    descriptions: {
        type: [String],
        required: false,
    },
    deleted: {
        type: Boolean,
        required: false,
        default: false,
    }
}, {timestamps: true});

Oh, and by the way: using Types.ObjectId instead of string throws the same error.

I have absolutely no idea how to correctly design the schema as long as the documentation keeps silent on that.

@mjfwebb
Copy link

mjfwebb commented Oct 24, 2021

@rongem I agree and have a lot of struggles with the lack of documentation for Typescript.

One thing though, I have had some success using Schema.Types.ObjectId instead of Types.ObjectId.

Hope that helps.

@vkarpov15
Copy link
Collaborator

@rongem I opened up a separate issue #10949 to track this

@Automattic Automattic locked as resolved and limited conversation to collaborators Nov 2, 2021
@vkarpov15
Copy link
Collaborator

@rongem that's because you're defining your schema incorrectly. Like @mjfwebb said, replace:

const historicConnectionSchema = new Schema<IHistoricConnection,  Model<IHistoricConnection>>({
    connectionRuleId: {
        type: Types.ObjectId,
        required: true,
    },
    // ...
})

with:

const historicConnectionSchema = new Schema<IHistoricConnection,  Model<IHistoricConnection>>({
    connectionRuleId: {
        type: Schema.Types.ObjectId, // <-- ObjectId SchemaType, **not** the actual ObjectId constructor
        required: true,
    },
    // ...
})

Type errors are expected in this case.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
typescript Types or Types-test related issue / Pull Request
Projects
None yet
Development

No branches or pull requests

7 participants