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

Type transformations do not use concrete types #37401

Open
aloifolia opened this issue Mar 15, 2020 · 2 comments
Open

Type transformations do not use concrete types #37401

aloifolia opened this issue Mar 15, 2020 · 2 comments
Labels
Needs More Info The issue still hasn't been fully clarified

Comments

@aloifolia
Copy link

So, I am not quite sure if there is already a way around this behaviour or if this is a feature request.

My code makes heavy use of type computations via recursive conditional types and mapped types. These types generate stuff like

// VSCode shows that Generated resolves to { a: { b1: { c: 42 }, b2: { d: { e:  42}}}}
type Generated = ComplicatedTransformation<SomeType> 

This works until I make use of these generated types by transforming them even further. E.g., there is

type OmitNever<T> = Omit<T, SelectKeys<T, never>>;

As soon as I feed the generated type Generated to OmitNever the compiler shows the infamous error message Type instantiation is excessively deep and possibly infinite (#31619).

Now I was wondering why the compiler does not use the concrete type that VSCode shows when the cursor hovers over Generated. Instead it seems to recalculate the whole (or part) of ComplicatedTransformation.

Is there a way to tell the compiler "use the generated type as is and just forget how this type was generated"? If not, would that be a useful addition (in my eyes it looks like that, but maybe I am missing something crucial)?

@AnyhowStep
Copy link
Contributor

AnyhowStep commented Mar 15, 2020

There are many ways to get around that error. And all of them are situational, and some of them unintuitive.

There are a bunch of ways to trigger the error. And the fix also depends on what the trigger is.

  • One massive type computation
    • Recursive
    • Non-Recursive
  • Many methods chained
  • Many functions nested
  • Few/Some/Many no-ops
  • Large Union
  • Deeply nested conditionals
  • Long tuples
  • Deeply nested object hierarchy
  • etc.

Is there a way to tell the compiler "use the generated type as is and just forget how this type was generated"?

You may be able to do it sometimes,

type MyType<ArgsT> = Complicated<ArgsT> extends infer R ? R : never

Just note that conditional types and infer may cause TS to be unable to determine assignability in some cases when you, the human, know they're perfectly assignable.

There's also,

type TryReuse<ExistingT, NewerT> = IsEqual<ExistingT, NewerT> extends true ? ExistingT : NewerT;
//If complicated<> resolves to ArgsT, reuse ArgsT
type MyType<ArgsT> = TryReuse<ArgsT, Complicated<ArgsT>>;

There's also a tonne of other weird hacks. Don't ask me why they work. I discovered them and tested them by trying everything, even the nonsensical things.

Look at my issue history on this repo (author:anyhowstep), I've got a tonne of weird issues related to max depth (getting that error, or max stack exceeded, or types resolving to any when it should get max depth error) where the workaround is unintuitive but actually works


Overall, the best thing to do is to not write complicated types in the first place. Because if you do, you may spend many hours trying to get around the max depth error.

And if you do get it to compile, then you end up trying to find ways to speed up compile times because you realize your projects using your complicated types take 20+ minutes to build

@RyanCavanaugh RyanCavanaugh added the Needs More Info The issue still hasn't been fully clarified label Mar 16, 2020
@RyanCavanaugh
Copy link
Member

Type aliases are generally "just" aliases and behave as if they are inlined at their use sites; this is important to make other scenarios work.

If you have a case where it looks like the instantiation process should be finite and shallow, we can investigate as a one-off, but in general the error means what it says. At some point it's best to switch to a .d.ts generation process if your types are extraordinarily complex.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs More Info The issue still hasn't been fully clarified
Projects
None yet
Development

No branches or pull requests

3 participants