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

4.7 regression: Array.flat(infinity) leads to "Type instantiation is excessively deep and possibly infinite." error #49280

Open
peterjuras opened this issue May 27, 2022 · 8 comments
Labels
Bug Cursed? Help Wanted
Milestone

Comments

@peterjuras
Copy link

@peterjuras peterjuras commented May 27, 2022

Bug Report

πŸ”Ž Search Terms

  • Array.flat
  • Type instantiation is excessively deep and possibly infinite.
  • Nested arrays

πŸ•— Version & Regression Information

  • This changed between versions 4.6 and 4.7

⏯ Playground Link

TypeScript Workbench with relevant code

πŸ’» Code

interface Config {
  prop?: string;
}

interface ExtendedConfig extends Config {}

type NestedConfigs = Array<ExtendedConfig | NestedConfigs>;

const configs: NestedConfigs[] = [];

const flattened = configs.flat(Infinity);

πŸ™ Actual behavior

TypeScript raises the following error for the configs.flat(Infinity) call:

Type instantiation is excessively deep and possibly infinite. ts(2589)

πŸ™‚ Expected behavior

No error should be raised.

@grant-dennison
Copy link

@grant-dennison grant-dennison commented May 27, 2022

Ran into this today as well in 4.7.2. 4.6 typed the output as any[]. (playground)

@sepiette
Copy link

@sepiette sepiette commented May 30, 2022

also ran into this in an upgrade from 4.6.4 to 4.7.2. Entire app won't compile as a result.

@taylorfsteele
Copy link

@taylorfsteele taylorfsteele commented May 31, 2022

Just ran into this as well, here's a very concise example:

type StringOrStringArray = string | StringOrStringArray[];

const flatten = (...args: StringOrStringArray[]) => args.flat(Infinity);

Same exact error on args.flat(Infinity), "Type instantiation is excessively deep and possibly infinite."

A quick solution for now is to assert your .flat argument as an integer:

args.flat(Infinity as 10)

Should make the error go away.

@RyanCavanaugh
Copy link
Member

@RyanCavanaugh RyanCavanaugh commented Jun 1, 2022

I'm not quite sure how to reason about this. This error is, for all intents and purposes, correct. You could have written

interface Config {
  prop?: string;
}

interface ExtendedConfig extends Config {}

type NestedConfigs = Array<ExtendedConfig | NestedConfigs>;

const configs: NestedConfigs[] = [];
configs.push(configs); // <- legal
const flattened = configs.flat(Infinity); // <- crashes at runtime

which causes

VM273:1 Uncaught RangeError: Maximum call stack size exceeded
    at Array.flat (<anonymous>)
    at <anonymous>:1:5

If you have a finitely-deep (T | T[])[] then it seems best to pass a finite argument to flat. Type-asserting Infinity to a lower integer (I'd really recommend 1, not 10, as they actually do different levels of unwrapping) if you're confident that the depth is actually bounded by some unknowably-high integer (????) seems like an appropriate assertion, since you know more than TypeScript does about the data structure in this case.

@grant-dennison
Copy link

@grant-dennison grant-dennison commented Jun 1, 2022

I'm not sure what I think the correct type of the output is, but it does seem to me that the TypeScript compiler should allow .flat(Infinity) (without type assertion) without straight-up failing the compile since it is valid and common (e.g. example on MDN) JavaScript.

Maybe it would make sense to special-case Infinity to wind up with unknown[] or any[]?

@grant-dennison
Copy link

@grant-dennison grant-dennison commented Jun 1, 2022

I should also note that, as a user of TypeScript, the "possibly infinite" thing made enough sense to me, but I was a bit stumped how to get around the type error because this kind of error prevents doing a post-cast like configs.flat(Infinity) as Config[].

@taylorfsteele
Copy link

@taylorfsteele taylorfsteele commented Jun 1, 2022

Type-asserting Infinity to a lower integer (I'd really recommend 1, not 10, as they actually do different levels of unwrapping) if you're confident that the depth is actually bounded by some unknowably-high integer (????) seems like an appropriate assertion, since you know more than TypeScript does about the data structure in this case.

Oh, that's really interesting, I actually didn't know that. So to get more specific, in my use case I was actually using it within a function that allows one to optionally declare a flattening depth. To go off of my example, that might look like:

type StringOrStringArray = string | StringOrStringArray[];

const curriedFlatten = (depth = 10) => (...args: StringOrStringArray[]) => args.flat(depth); // <--- Same instantiation error

The intent here was to mimic something like a flat(Infinity), Hence the assertion to a higher integer. I'm assuming that because in this instance depth can be Infinity, that it gives that error. If TS treats the type-assertion with different levels of unwrapping, it might be worth it to lower that depth instantiation just for those gains.

I tend to agree with @grant-dennison above, it seems like .flat(Infinity) feels commonplace enough to warrant TypeScript handling this in a way that makes sense with (T | T[])[] types for flattening them. That being said, I do see how that gets very hairy, very fast.

A potential middle-ground here may be some form of 'non-infinite' integer or number type. To go back to my example:

type StringOrStringArray = string | StringOrStringArray[];

const fancyFlatten = (depth: NonInfiniteNumber, ...args: StringOrStringArray[]) => args.flat(depth);

That way I can tell TypeScript that this is actually bounded by some unknowably-high integer, and have type errors if I try to call fancyFlatten with Infinity

@RyanCavanaugh RyanCavanaugh added Bug Help Wanted Cursed? labels Jun 1, 2022
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Jun 1, 2022
@tjjfvi
Copy link
Contributor

@tjjfvi tjjfvi commented Jun 2, 2022

This could be addressed if a literal type for Infinity was added: playground link using 1e309

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

No branches or pull requests

6 participants