devanshj/sthir

"Type instantiation is excessively deep" for `Element` as a predicant

devanshj opened this issue · 6 comments

import { p, pa } from "@sthir/predicate"

pa({} as Element, p())
//                ~~~
// Type instantiation is excessively deep and possibly infinite.(2589)

Not sure why it's happening, possibly because Element has many keys... If that's the reason then we need to figure out a test for type with many keys (or union with many constituents) and then don't compute index operator for them (which means now .parentNode, .children, etc won't show up in the intellisense completetion) and only validate it.

I think we can find if a union has many constituents (say more than 15) using something like this...

namespace U {
  type HasManyConstituents<U, L = 15> =
    U extends never ? false :
    L extends 0 ? true :
    HasManyConstituents<U.Pop<U>, N.Decrement<L>>
}

And later we could even take the upper limit from some "options interface" via module augumentation instead of keeping it a hardcoded 15.

Also I wonder if it's possible make sense to show only top 15 keys as completion but also have a .${string} that we later validate, so like ".parentNode" | ".children" | `.${string}` (with validation for `.${string}`)

Another much greater possibility is that (afair) we compute index operator deeply (and eagerly) and Element has a recursive definition hence we get an infinite recursion. In that case we should just not compute the index operator eagerly for all cases (or maybe not just in cases where predicant has a recursive definition)

Yep, looks like it's because of recursive definition...

import { p, pa } from "@sthir/predicate"

interface MyElement {
  parentElement: MyElement
}

pa({} as MyElement, p())
//                  ~~~
// Type instantiation is excessively deep and possibly infinite.(2589)

In that case let's do this...

In that case we should just not compute the index operator eagerly for all cases (or maybe not just in cases where predicant has a recursive definition)

I'm too lazy to rewrite the types right now to make index not compute eagerly, so instead in case of recursive types we stop when we come across a type we've already seen and with that we fixed #8

This comes with a little downside that instead of being able to write pa(e, p(".parentElement.parentElement.innerHTML")) you'll have to write pa(e, p(".parentElement .parentElement.innerHTML")) basically you'll have to break the index operator at the point of recursion.

But even after fixing #8 the code in the description takes too long (and probably also shows the error didn't wait to see it) that's probably because as we're still computing (deep) index eagerly and Element is huge af.

Now to fix Element scenario and also remove the breaking-at-point-of-recursion workaround we'll have to have non-eager index operator.

We can also try another solution of checking when a type is huge and making users break the index operator at every level (p(".a .b .c") instead of p(".a.b.c")), that might be easier.

e4dfae5 — Pretty weird even with PathShallow we still get the "Type instantiation is excessively deep" error. This will require more investigation lmao.

Ah maybe I.Intersect also needs to deal with recursive definitions.