question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Max depth limit does not trigger. Gives up and resolves type to any

See original GitHub issue

TypeScript Version: 3.5.1

Search Terms:

  • Type instantiation is excessively deep and possibly infinite.ts(2589)
  • Chaining methods
  • Fluent API
  • Generic class
  • Generic method

Code

Using DataT,

type AppendToArray<ArrT extends any[], T> = (
    (
        ArrT[number]|
        T
    )[]
);
interface Data {
    arr : number[]
};
type AppendToFoo<C extends Data, N extends number> = (
    Foo<{arr:AppendToArray<C["arr"], N>}>
);

class Foo<DataT extends Data> {
    arr! : DataT["arr"];
    //Using `DataT`
    x<N extends number> (n:N) : AppendToFoo<DataT, N> {
        return null!;
    }
}
declare const foo0 : Foo<{arr:0[]}>;
/*
const foo12: Foo<{
    arr: (0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12)[];
}>
*/
const foo12 = foo0
    .x(1).x(2).x(3).x(4).x(5).x(6).x(7).x(8).x(9).x(10).x(11).x(12);
/*
Expected:

+ Similar to foo12
OR
+ Type instantiation is excessively deep and possibly infinite.ts(2589)

Actual:
const foo13: Foo<{
    arr: any[];
}>
*/
const foo13 = foo12.x(13);

Playground

Using this,

type AppendToArray<ArrT extends any[], T> = (
    (
        ArrT[number]|
        T
    )[]
);
interface Data {
    arr : number[]
};
type AppendToFoo<C extends Data, N extends number> = (
    Foo<{arr:AppendToArray<C["arr"], N>}>
);

class Foo<DataT extends Data> {
    arr! : DataT["arr"];
    //Using `this`
    x<N extends number> (n:N) : AppendToFoo<this, N> {
        return null!;
    }
}
declare const foo0 : Foo<{arr:0[]}>;
/*
const foo9: Foo<{
    arr: (0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9)[];
}>
*/
const foo9 = foo0
    .x(1).x(2).x(3).x(4).x(5).x(6).x(7).x(8).x(9);
/*
Expected:

+ Similar to foo9
OR
+ Type instantiation is excessively deep and possibly infinite.ts(2589)

Actual:
const foo10: Foo<{
    arr: any[];
}>
*/
const foo10 = foo9.x(10);

Playground

Expected behavior:

Both examples should resolve correctly or trigger, Type instantiation is excessively deep and possibly infinite.ts(2589)

See code for more details.

Actual behavior:

Both examples resolve to any without errors. Both examples seem to have a different limit before this happens?

See code for more details.

Playground Link:

Using DataT, the limit seems to be 12.

Playground

Using this, the limit seems to be 9.

Playground

Related Issues:

I have no clue how to search for this.


I came across this weird bug by accident, actually.

I was working on a personal project and this would be the 4th, or 6th time I’ve rewritten the project. I’ve noticed that in all the rewrites, there’s this particular method on generic class where the max number of times I could chain it was always 20.

The 21st call would always trigger the max depth error.

I was sick of it and decided to investigate possible ways to work around this limit.

I decided to write a simple repro before messing with it. (The above code snippets). However, the simplified repro behaved very weirdly, and would not trigger the error.

It boggles me how TS can resolve crazy types like this,

image

but will choke on simple types like the above snippets.


It’s also super weird because my super complex examples have a limit of 20 calls. And these super simple examples have a super low limit.

Issue Analytics

  • State:open
  • Created 4 years ago
  • Comments:18 (18 by maintainers)

github_iconTop GitHub Comments

1reaction
weswighamcommented, Aug 5, 2019

Absolutely - the depth limit is as low as it is simply because if it were much higher you’d get a stack overflow because instantiation is written as a set of mutually recursive functions. That’s why I opened https://github.com/microsoft/TypeScript/pull/32611 - all we really want to limit is really complex types (instantiation is effectively type execution, given conditionals, and we do not want to let types execute forever ❤️ ), not deep types, and using a trampoline allows us to remove the stack limit-based constraint.

1reaction
AnyhowStepcommented, Aug 5, 2019

@weswigham

Here’s an even more mind boggling example,

type AppendToArray<ArrT extends any[], T> = (
    (
        ArrT[number]|
        T
    )[]
);
interface Data {
    arr : number[]
};
/*
    Uses object instead of `number[]`
*/
interface InterfaceFoo<DataT extends Data> {
    arr : DataT["arr"];
}
type AppendToFoo<C extends Data, N extends number> = (
    ClassFoo<{arr:AppendToArray<C["arr"], N>}>
);

/*
    Uses object instead of `number[]`
*/
class ClassFoo<DataT extends Data> implements InterfaceFoo<DataT> {
    arr! : DataT["arr"];
    //Using `this`
    x<N extends number> (n:N) : AppendToFoo<this, N> {
        return null!;
    }
}

declare const foo0 : ClassFoo<{arr:0[]}>;
/*
const foo9: ClassFoo<{
    arr: (0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9)[];
}>
*/
const foo9 = foo0
    .x(1).x(2).x(3).x(4).x(5).x(6).x(7).x(8).x(9);
/*
Expected:
Max depth error OR resolve correctly

Actual:
const foo10: ClassFoo<{
    arr: any[];
}>
*/
const foo10 = foo9.x(10);
/*
Expected:
Max depth error OR resolve correctly

Actual:
const foo20: ClassFoo<{
    arr: any[];
}>
*/
const foo20 = foo10.x(11).x(12).x(13).x(14).x(15).x(16).x(17).x(18).x(19).x(20);
/*
Expected:
Max depth error OR resolve correctly

Actual:
const foo24: ClassFoo<{
    arr: any[];
}>
*/
const foo24 = foo20.x(21).x(22).x(23).x(24);
/*
I finally get,
Type instantiation is excessively deep and possibly infinite.
*/
const foo25 = foo24.x(25);
//            ~~~~~~~~~~~~ Type instantiation is excessively deep and possibly infinite.

Playground

It is similar to the WhyDoesThisWorkFoo<> example because you get the max depth error at .x(25).

However it is also similar to the original example because .x(10) onward gives you any.

What’s mind boggling is that you do not get any errors and still get any from .x(10) till .x(24).

I have no idea why.


Expected: .x(10) onward should give error (or resolve correctly).

Actual:

  • .x(10) onward gives any without error.
  • .x(25) onward gives max depth error. (Just like the WhyDoesThisWorkFoo<> example)

@fatcerberus From 10 to 24, it’s like the compiler is in a superposition of having given up and not given up on resolving the type.

I dub it Schrödinger’s type resolution

Read more comments on GitHub >

github_iconTop Results From Across the Web

ReactJS: Maximum update depth exceeded error
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate.
Read more >
Limits and configuration - Power Automate - Microsoft Learn
Concurrency, looping, and debatching limits ; Until iterations, - Default: 60 - Maximum: 5,000 ; Paginated items, 5,000 for Low, 100,000 for all ......
Read more >
Quotas and limits | BigQuery - Google Cloud
This process continues until all tables in the dataset are copied, up to the maximum of 20,000 tables per dataset. Reservations. The following...
Read more >
sys — System-specific parameters and functions — Python ...
Raise an auditing event and trigger any active auditing hooks. event is a string ... The number and types of arguments for a...
Read more >
User Guide :: Nsight Systems Documentation
In this case, setting a flush interval can reduce memory use by the profiler but may increase save overhead. For collections over 30...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found