Home > Software engineering >  Height of binary tree type instantiation is excessive
Height of binary tree type instantiation is excessive

Time:11-12

I'm playing around in the type system and I'm currently trying to make a reverse level-order traversal at the type level. These are the types I'm working with:

type LEFT = 0;
type VALUE = 1;
type RIGHT = 2;

type List = ReadonlyArray<unknown>;

type Increment<N extends List> = [...N, unknown];
type Decrement<N extends List> = N extends readonly [...infer H, any] ? H : never;

// `Node` already exists as a built-in... so I used Deno...
type Deno = [LEFT: Deno | undefined, VALUE: number, RIGHT: Deno | undefined];

I'm using a tuple instead of an object type to represent nodes since it'll be easier to create and change testing values. I also have some aliases to make it more "readable" when accessing the data (MyDeno[LEFT] is easier to understand than MyDeno[0]). Also, I'm using tuples to represent numbers, since they're much easier to work with and don't need a limit to be manually specified.

I can get the height of a tree in JavaScript like this:

function height(node) {
  if (!node) return 0;

  const lhs = height(node.left);
  const rhs = height(node.right);

  return Math.max(lhs, rhs)   1;
}

But my attempt at transferring this to the type system generates an error:

type Max<A extends List, B extends List> = A extends [...B, ...any[]] ? A : B;

type Height<Root extends Deno | undefined> =
    Root extends Deno
        ? Max<Increment<Height<Root[LEFT]>>, Increment<Height<Root[RIGHT]>>>
//            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Type instantiation is excessively deep and possibly infinite.
        : [];

even though it works with sample data:

type Data = [
    [
        [undefined, 4, undefined],
        2,
        [undefined, 5, undefined],
    ],
    1,
    [undefined, 3, undefined],
];

// Should be 3 since the height of `Data` is 3
type T = Height<Data>["length"];
//   ^? 3

I wasn't expecting this to throw "Type instantiation is excessively deep"... Where in my type is the fault, and how could I refactor my Height type so that it doesn't throw this error? Until I can correct this, I'll stick with using //@ts-ignore.

Playground


Completed implementation

CodePudding user response:

I've noticed that sometimes when recursive conditional types are distributive there is a greater chance of hitting instantiation depth limits than there is when the type is non-distributive.

Thus, one approach I take is to see if I can proceed by turning off distributivity over unions (via the standard one-tuple-wrapper technique X extends Y ? A : B[X] extends [Y] ? A : B):

type Height<Root extends Deno | undefined> =
    [Root] extends [Deno]
    ? Max<Increment<Height<Root[LEFT]>>, Increment<Height<Root[RIGHT]>>>  // okay
    : [];

That works, at least for your example code.

Playground link to code

  • Related