Recursive tuple cannot be computed through generic, but valid in a declarative form
Opened this issue ยท 2 comments
๐ Search Terms
ts2589 "Type instantiation is excessively deep and possibly infinite"
๐ Version & Regression Information
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about ts2589
- I was unable to test this on prior versions before v4.7.0 because
infer T extends Rsyntax not introduced
โฏ Playground Link
๐ป Code
type Foo = {
leading: 'a',
rest: [Foo],
};
type Expand<T> = T extends {
leading: infer L;
rest: infer R extends any[]
} ? [L, ...ExpandMany<R>] : never;
type ExpandMany<T extends any[]> =
T extends [infer First, ...infer Rest] ? [Expand<First>, ...ExpandMany<Rest>] : [];
type TestA = Expand<Foo>;
// ^~ Type instantiation is excessively deep and possibly infinite.(2589)
type TestB = ['a', TestB];๐ Actual behavior
TestA meant to be:
TestA
-> Expand<Foo>
-> ['a', ...ExpandMany<[Foo]>]
-> ['a', ...[Expand<Foo>]]
-> ['a', Expand<Foo>]
-> ['a', TestA]
which is identical to TestB but TS complains the instantiation cannot stop.
๐ Expected behavior
Stop expansion at first reference at appeared structure Expand<Foo>, that is:
type TestA = Expand<Foo>;
// ^?~ type TestA = ['a', Expand<Foo>]
// or more intelligent `type TestA = ['a', TestA]`
Additional information about the issue
This code is useful when Foo is as const inferred from a runtime constant, and we want a tuple-like typing calculated from Foo. Since TypeScript support self-referential tuple by declarative just like TestB, it would be better for support that in a computed way like Expand<Foo>.
๐ค Thank you for your issue! I've done some analysis to help get you started. This response is automatically generated; feel free to ๐ or ๐ this comment according to its usefulness.
Similar Issues
Here are the most similar issues I found
- (71%) microsoft/typescript#54910: Type instantiation is excessively deep with tuple spread
- (71%) microsoft/typescript#39992: Unjustified and undetected circular type definition when using one-tuple to prevent distributive conditional type with a condition
- (68%) microsoft/typescript#41771: Type checker hangs on recursive type with arrays
- (68%) microsoft/typescript#25291: Stack overflow when spread a tuple type alias inside itself (with conditional type and generic default)
- (67%) microsoft/typescript#31619: "Type instantiation is excessively deep and possibly infinite" even for generated non-recursive type
- (67%) microsoft/typescript#48401: Source provides no match for required element at position 0 in target despite source extending [any, ...any[]]
- (66%) microsoft/typescript#54550: Generic Tuple Optional Parameter spread errors with Type instantiation is excessively deep and possibly infinite. ts(2589)
- (66%) microsoft/typescript#40298: Recursive type references with spreading array of self causes circularly error
- (66%) microsoft/typescript#40022: recursive deep property access works unless the assigned variable has explicit type annotation?
- (66%) microsoft/typescript#40229: Treatment of recursive tuples that utilize type spread.
- (65%) microsoft/typescript#59737: `instantiateMappedTupleType` should not use array index as mapping key, at least it's not in an appropriate way
- (65%) microsoft/typescript#58661: Recursive type strangely resolved to never
- (65%) microsoft/typescript#46500: TS 4.5 type instantiation is excessively deep and possibly infinite, with seemingly legal type
If your issue is a duplicate of one of these, feel free to close this issue. Otherwise, no action is needed.
Not sure what's going wrong here; the stack indicates a runaway instantiating ExpandMany<Rest>.
Alternative form that works the same:
type Expand<T> = T extends {
leading: unknown;
rest: any[]
} ? [T['leading'], ...T['rest']] : never;
type ExpandMany<T> = { [K in keyof T]: Expand<T[K]> };