typescript-go icon indicating copy to clipboard operation
typescript-go copied to clipboard

Errors in @types/jsdom, @sinclair/typebox

Open andyfleming opened this issue 7 months ago • 2 comments

npx tsgo src
node_modules/@sinclair/typebox/typebox.d.ts:137:63 - error TS2321: Excessive stack depth comparing types 'UnionToTuple<{ [K in T]: TLiteral<K>; }[T], UnionLast<{ [K in T]: TLiteral<K>; }[T]>>' and 'TSchema[]'.

137 export type TExcludeTemplateLiteralResult<T extends string> = TUnionResult<Assert<UnionToTuple<{
                                                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
138     [K in T]: TLiteral<K>;
    ~~~~~~~~~~~~~~~~~~~~~~~~~~
139 }[T]>, TSchema[]>>;
    ~~~~~~~~~~~~~~~~~~
node_modules/@sinclair/typebox/typebox.d.ts:145:63 - error TS2321: Excessive stack depth comparing types 'UnionToTuple<{ [K in T]: TLiteral<K>; }[T], UnionLast<{ [K in T]: TLiteral<K>; }[T]>>' and 'TSchema[]'.

145 export type TExtractTemplateLiteralResult<T extends string> = TUnionResult<Assert<UnionToTuple<{
                                                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
146     [K in T]: TLiteral<K>;
    ~~~~~~~~~~~~~~~~~~~~~~~~~~
147 }[T]>, TSchema[]>>;
    ~~~~~~~~~~~~~~~~~~
node_modules/@sinclair/typebox/typebox.d.ts:380:102 - error TS2321: Excessive stack depth comparing types 'UnionToTuple<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>' and 'TLiteral<TLiteralValue>[]'.

380 export type TUnionTemplateLiteral<T extends TTemplateLiteral, S extends string = Static<T>> = Ensure<TUnionResult<Assert<UnionToTuple<{
                                                                                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
381     [K in S]: TLiteral<K>;
    ~~~~~~~~~~~~~~~~~~~~~~~~~~
382 }[S]>, TLiteral[]>>>;
    ~~~~~~~~~~~~~~~~~~~
node_modules/@types/jquery/misc.d.ts:7359:14 - error TS2717: Subsequent property declarations must have the same type.  Property 'toStringTag' must be of type 'unique symbol', but here has type 'symbol'.

7359     readonly toStringTag: symbol;
                  ~~~~~~~~~~~

  node_modules/@typescript/native-preview-darwin-arm64/lib/lib.es2015.symbol.wellknown.d.ts:74:14 - 'toStringTag' was also declared here.
    74     readonly toStringTag: unique symbol;
                    ~~~~~~~~~~~
node_modules/@types/jsdom/base.d.ts:194:18 - error TS2411: Property 'Infinity' of type 'number' is not assignable to 'number' index type 'Window'.

194         readonly ["Infinity"]: number;
                     ~~~~~~~~~~~~
node_modules/@types/jsdom/base.d.ts:195:18 - error TS2411: Property 'NaN' of type 'number' is not assignable to 'number' index type 'Window'.

195         readonly ["NaN"]: number;
                     ~~~~~~~

Found 6 errors in 3 files.

Errors  Files
     3  node_modules/@sinclair/typebox/typebox.d.ts:137
     1  node_modules/@types/jquery/misc.d.ts:7359
     2  node_modules/@types/jsdom/base.d.ts:194

andyfleming avatar May 23 '25 21:05 andyfleming

Can you provide a short repro?

ahejlsberg avatar May 28 '25 20:05 ahejlsberg

Here's a minimal reproduction:

https://github.com/andyfleming/repro-for-typescript-go-929

andyfleming avatar May 29 '25 18:05 andyfleming

@andyfleming u might have forgotten to push some files there

Andarist avatar Jun 05 '25 17:06 Andarist

@andyfleming u might have forgotten to push some files there

@Andarist A fresh clone of that repository and running the commands in the README reproduces the errors for me.

andyfleming avatar Jun 05 '25 18:06 andyfleming

That's not what it was asked for here. A short repro would be a self-isolated one, preferably within a single file. It shouldn't rely on massive external packages

Andarist avatar Jun 06 '25 10:06 Andarist

@Andarist @jakebailey Hello,

I have just happened across this issue. As per request, I have setup a repro specifically for TypeBox at the link below:

https://github.com/sinclairzx81/typescript-go-issue-929

I should note that the referenced TypeBox version 0.27.8 is extremely out of date. The issue can be resolved by updating TypeBox to the latest 0.34.33. Unfortunately, there is a very long tail of downstream libraries and tools that do take a dependency on older versions of Jest (which in turn take the dependency to 0.27.8). Given the dependency chains downstream, it's not entirely trivial for applications to just update to the latest versions to leverage TS native.

Happy to provide additional information if required. S

sinclairzx81 avatar Jun 10 '25 10:06 sinclairzx81

The best thing you could provide is a self-isolated repro of the problem. This one still depends on an external library, one that has a lot of unrelated (to the problem) types in it. Investigating such repro takes considerably more time than investigating a small one.

Andarist avatar Jun 10 '25 11:06 Andarist

The best thing you could provide is a self-isolated repro of the problem. This one still depends on an external library, one that has a lot of unrelated (to the problem) types in it. Investigating such repro takes considerably more time than investigating a small one.

@Andarist Project has been updated to include only offending types. Library dependencies have been removed as per request.

https://github.com/sinclairzx81/typescript-go-issue-929

sinclairzx81 avatar Jun 10 '25 11:06 sinclairzx81

This is great and way more actionable. Thanks ❤ I'll investigate this in the coming days

Andarist avatar Jun 10 '25 14:06 Andarist

My preliminary findings...

This is a type ordering issue. The type arguments are being normalized and thus simplified. getTrueTypeFromConditionalType in getSimplifiedConditionalType leads to relating [...UnionToTuple<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>, UnionLast<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<...>>>>, UnionLast<...>] source to TLiteral<string>.

Here: https://github.com/microsoft/typescript-go/blob/9cf6764c67542f4b764d227d5195cdf348a03702/internal/checker/relater.go#L3779

We can observe:

source // [...UnionToTuple<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>, UnionLast<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<...>>>>, UnionLast<...>]
target // TLiteral<string>[]

And we enter isRelatedTo with their index info types:

originalSource // UnionLast<{ [K in S]: TLiteral<K>; }[S]> | UnionToTuple<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>, UnionLast<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>>>[number]
originalTarget // TLiteral<string>

Buuut... in Corsa the order of this originalSource union is flipped. This ends up in eachTypeRelatedToType and the whole relation ends up returning Ternary.False (in Strada too) but in Corsa it relates against the first constituent, well, first. That leads to deep recursion when the constraint of this source gets related here: https://github.com/microsoft/typescript-go/blob/9cf6764c67542f4b764d227d5195cdf348a03702/internal/checker/relater.go#L3608-L3609

We can observe there:

source // UnionToTuple<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>, UnionLast<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>>>[number]
constraint // ([] | [...UnionToTuple<Exclude<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>, UnionLast<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>>>, UnionLast<Exclude<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>, UnionLast<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>>>>>, UnionLast<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>>])[number]
target // TLiteral<string>

When the compiler hits it again those can be observed:

source // UnionToTuple<Exclude<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>, UnionLast<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>>>, UnionLast<Exclude<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>, UnionLast<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>>>>>[number]
constraint // ([] | [...UnionToTuple<Exclude<Exclude<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>, UnionLast<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>>>, UnionLast<Exclude<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>, UnionLast<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>>>>>, UnionLast<Exclude<Exclude<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>, UnionLast<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>>>, UnionLast<Exclude<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>, UnionLast<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>>>>>>>, UnionLast<Exclude<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>, UnionLast<Exclude<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>>>>])[number]
target // TLiteral<string>

As we can see, the source side here just "grows" through recursive attempts at relating its constraint to the target.

So, in a sense, Strada was able to return early from this as it related a different source first. Note that all of this is just within the normalization/simplification of the source. It can't be simplified - but it has to relate types to check if it can or not. And that leads to the dreaded "Excessive stack depth comparing types"

Andarist avatar Jun 10 '25 18:06 Andarist

Unfortunately this is only getting worse becuase @jest/schemas depends on @sinclair/typebox and so now any project using jest fails without skipLibCheck from:

node_modules/.pnpm/@[email protected]/node_modules/@sinclair/typebox/typebox.d.ts:380:102 - error TS2321: Excessive stack depth comparing types 'UnionToTuple<{ [K in S]: TLiteral<K>; }[S], UnionLast<{ [K in S]: TLiteral<K>; }[S]>>' and 'TLiteral<TLiteralValue>[]'.

380 export type TUnionTemplateLiteral<T extends TTemplateLiteral, S extends string = Static<T>> = Ensure<TUnionResult<Assert<UnionToTuple<{
                                                                                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
381     [K in S]: TLiteral<K>;
    ~~~~~~~~~~~~~~~~~~~~~~~~~~
382 }[S]>, TLiteral[]>>>;
    ~~~~~~~~~~~~~~~~~~~

I hit this while testing build mode in DefinitelyTyped-tools.

jakebailey avatar Aug 21 '25 23:08 jakebailey