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. #24

Closed
gbouteiller opened this issue Jul 28, 2023 · 16 comments
Closed

Type instantiation is excessively deep and possibly infinite. #24

gbouteiller opened this issue Jul 28, 2023 · 16 comments
Assignees
Labels
bug Something isn't working external Problem has external origin workaround Workaround fixes problem

Comments

@gbouteiller
Copy link

Hello and first of all, thank you for your work with this library.
I was eager to migrate from zod to valibot after encountering this error on the use of schemas with many union and transform but the same one seems to occur.
Is there maybe a good practice to avoid it?
Thank you

@fabian-hiller
Copy link
Owner

Can you send me some code examples?

@fabian-hiller fabian-hiller self-assigned this Jul 28, 2023
@fabian-hiller fabian-hiller added the question Further information is requested label Jul 28, 2023
@gbouteiller
Copy link
Author

gbouteiller commented Jul 29, 2023

I have created a repo with my code.
For context, it's a bunch of schemas for the Notion API as well as a fetch wrapper.
My intent with this library is to validate the API responses and use schema transformations to simplify the structure sent by Notion.

  • The schemas/inputs contains the schemas I constructed and refactored from the API results (Notion API has a lot of union types).
  • The schemas contains the transformations.
  • The problem occurs in page
Capture d’écran 2023-07-29 à 09 14 19
  • If I comment properties which is a record of an union of many schemas, the problem disappears :
export const vIPage = merge([
  vIPageOrDatabaseCommon,
  object({
    object: literal('page'),
    parent: union([vIParentDatabase, vIParentPage, vIParentWorkspace]),
    // properties: record(vIPageProp),
  }),
]);

@fabian-hiller
Copy link
Owner

Yes, the problem is vIPageProp which is used with properties: record(vIPageProp) in vIPage. However, I have not found out the exact cause yet. I will investigate this further.

@fabian-hiller
Copy link
Owner

fabian-hiller commented Jul 29, 2023

I was able to reproduce the problem. However, I could not find the bug yet. I will investigate further in a moment. It is interesting to note that the problem does not occur in TypeScript v4.9.5. Therefore, it could also be a bug in TypeScript v5.

import { merge, omit, object, union, transform, record, string } from "valibot";

const Merge1 = merge([object({}), object({ key: string() })]);

const Merge2 = merge([Merge1, object({})]);

const Object1 = merge([Merge2, object({})]);

const Object2 = merge([omit(Merge2, ["key"]), object({})]);

const Record = record(union([Object1, Object2]));

transform(Record, (a) => a); // Error: Type instantiation is excessively deep and possibly infinite.ts(2589)

@fabian-hiller
Copy link
Owner

So as a workaround, you could downgrade TypeScript. It is important that you set the TypeScript version in VS Code to the version in your package.json. If you don't know how to do that and can't figure it out yourself, I can explain it to you here.

@fabian-hiller fabian-hiller added workaround Workaround fixes problem external Problem has external origin bug Something isn't working and removed question Further information is requested labels Jul 29, 2023
@fabian-hiller
Copy link
Owner

I have thoroughly checked the source code of omit and merge and cannot find any problem. I also noticed that when I copy the source code of merge into the project and update the imports, the problem disappears, even though it is the same code.

Since I have similar problems with Modular Forms using TypeScript v5, I assume that it must be a bug in TypeScript. I can't explain it any other way at the moment. If anyone else wants to investigate the issue further, I'm happy to hear the results. Until then, for all other users, I recommend downgrading TypeScript to v4.9.5.

@gbouteiller
Copy link
Author

Thank you for your reactivity! Yes, I'll try to downgrade and see if everything works smoothly :) At the same time, I'll investigate about that problem too.

@gbouteiller
Copy link
Author

It should be related to :

@zkulbeda
Copy link
Contributor

zkulbeda commented Aug 5, 2023

The way it was resolved in kysely:

@fabian-hiller
Copy link
Owner

Thank you for the tip. I will try to investigate this in the next few weeks.

@octet-stream
Copy link

I'm getting the same error message when using with object + merge.

As a workaround I'm using ObjectSchema.object property, like this:

import {object} from "valibot"

import {FileOutput} from "./FileOutput.js"
import {ImageSize} from "./ImageSize.js"

export const ImageOutput = object({
  ...FileOutput.object,

  metadata: ImageSize
})

@fabian-hiller
Copy link
Owner

Yes, that works too. Thank you! Another workaround that works in most cases is intersection:

import { intersection, object, string } from 'valibot';

const Object1 = object({ key1: string() });
const Object2 = object({ key2: string() });
const Object3 = intersection([Object1, Object2]);

@mutewinter
Copy link

mutewinter commented Jan 13, 2024

I noticed marge slowdown in my TypeScript checking performance recently. I tracked it back to this and a switch from merge to intersect (the name has changed since this issue was created) cut my type checking time in half.

Running TypeScript v5.3.3 and Valibot v0.25.0.

@fabian-hiller
Copy link
Owner

Here is a guide that help to choose between intersect and merge: https://valibot.dev/guides/intersections/

@fabian-hiller
Copy link
Owner

I am toying with the idea of removing merge because of this TypeScript problem. Meanwhile, Valibot has a workaround that accomplishes exactly the same thing with a good DX. Please give me feedback.

import * as v from 'valibot';

const ObjectSchema1 = v.object({ key1: v.string() });

const ObjectSchema2 = v.object({ key2: v.number() });

const MergedSchema1 = v.merge([ObjectSchema1, ObjectSchema2]);

// This is the workaround that accomplishes exactly the same thing
const MergedSchema2 = v.object({
  ...ObjectSchema1.entries,
  ...ObjectSchema2.entries,
});

@fabian-hiller
Copy link
Owner

I think this is fixed in v0.30.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working external Problem has external origin workaround Workaround fixes problem
Projects
None yet
Development

No branches or pull requests

5 participants