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

[BUG] Type instantiation is excessively deep and possibly infinite on latest TypeScript 4.5 #715

Closed
mmvsk opened this issue Oct 19, 2021 · 8 comments

Comments

@mmvsk
Copy link

mmvsk commented Oct 19, 2021

There is a new issue that appeared in TypeScript 4.5.0-dev.20211001 and exists until now.

To reproduce the error:

npm init -y
npm add zod
npm add -D typescript@next
echo 'import { z } from "zod";' > main.ts
echo '{"compilerOptions":{"lib":["es2020"]},"include":["main.ts"]}' > tsconfig.json
npx tsc -p .

TypeScript output:

node_modules/zod/lib/helpers/partialUtil.d.ts:4:29 - error TS2589: Type instantiation is excessively deep and possibly infinite.

4         [k in keyof Shape]: ZodOptional<DeepPartial<Shape[k]>>;
                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


Found 1 error.

It affects the latest Zod (3.10.1), and also any other 3.x version. The issue appeared in microsoft/TypeScript#41821 (producing a memory error), and later a check was added in microsoft/TypeScript#46326 to prevent the infinite loop and give a meaningful message.

This is the same issue as #689 except that now we have an error message.

Demo code: https://github.com/mmvsk/zod-issue-715

@HadiSDev
Copy link

I am having the exact same issue:
image

@ryami333
Copy link
Contributor

ryami333 commented Oct 20, 2021

Are there any known workarounds? Besides not being able to upgrade to v4.5 I suspect that this might be the underlying performance problem responsible for an extra ~5seconds of type-checking per compile in my current project.

@mmvsk
Copy link
Author

mmvsk commented Oct 21, 2021

Are there any known workarounds? Besides not being able to upgrade to v4.5 I suspect that this might be the underlying performance problem responsible for an extra ~5seconds of type-checking per compile in my current project.

It's possible to use TS until 4.5.0-dev.20210930 (included):

npm add -D typescript@4.5.0-dev.20210930

The problem was caused by an internal refactoring of typescript (TS PR 41821), before that it was working (for me) perfectly fine, even in large projects.

@ryami333
Copy link
Contributor

ryami333 commented Oct 21, 2021

My understanding was that the refactoring surfaced these problems - it didn't actually introduce the underlying problem. In other words, this is not a Typescript bug, it's a feature.

It was/is also "working" for me in a fairly large project (total number of files processed per-compile is >1300), but we're having some pretty nasty Typescript performance issues which I've spent hours trying to debug. All that I've been able to deduce is that Zod seems to be at the heart of that problem. When I remove it, it cuts compile time in half and the total memory usage (as shown by tsc --diagnostics) falls off a cliff. And I'm not doing anything I would consider "complex", just 20-30 .object schemas for parsing JSON data. No circular schema or anything like that.

@colinhacks
Copy link
Owner

colinhacks commented Oct 23, 2021

Struggled with this for a solid day and it ended up being a one-line fix in ZodFormattedError. TS does not make it easy to identify the true source of these errors. I was getting worried that Zod had been taking advantage of a bunch of recursive type loopholes that had been closed, but I don't think that's the case.

Should be working with zod@3.11.1+

@mmvsk
Copy link
Author

mmvsk commented Oct 24, 2021

That's a good news!

By the way, the behavior of TS is strange here:

type F<T> = {} & (
    T extends [any, ...any[]]
        ? { [K in keyof T]?: F<T[K]> }
        : T extends any[]
            ? F<T[number]>[]
            : T extends { [K: string]: any }
                ? { [K in keyof T]?: F<T[K]> }
                : { x: unknown }
);

function f<T = any>() {
    return undefined! as F<T>;
}

export function g() {
    return f() as F<any>;
}

Changing anything removes the error:

  • return f<any>() as F<any>; (and it should be exactly the same as it's function f<T = any>())
  • return f();
  • replacing { x: unknown } with {}
  • removing T extends any[]
  • removing T extends [any, ...any[]]
  • or using F<T[number]>[] instead of { [K in keyof T]?: F<T[K]> }
  • TS 4.4...

To me it looks like an problematic edge case of TS 4.5, with a really low probability of being discovered...

@colinhacks
Copy link
Owner

Great minimal repro of the problem. I'm pretty sure we're the only people who will every be affected by it 😛

@mmvsk
Copy link
Author

mmvsk commented Oct 25, 2021

My thoughts exactly :)))

I posted this as a TS issue: microsoft/TypeScript#46500

And indeed, it's confirmed:

Yeah, that isn't right. The issue here is that we bypass caching of relations for small unions and intersections, but that bypass also causes us to loose a check for deeply nested types that would have stopped the recursion. It used to not matter, but after #41821 it does. We need to revise our logic such that we can bypass caching but still check for deep nesting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants