I'm looking to model a tree of nodes in TypeScript, where some are "element" nodes and some are "text" nodes (like in the DOM). This can be expressed as:
type Text = { text: string }
type Element = { children: Node[] }
type Node = Element | Text
And then I'm trying to write a TypeScript utility that can get the types of any potential descendant nodes that occur in the tree. Which I can write as:
type Descendants<N extends Node> = N extends { children: Array<infer T> }
? T extends Node
? T | Descendants<T>
: never
: never
Given a set of custom nodes:
type Paragraph = { children: Text[] }
type Quote = { children: Paragraph[] }
All of these work:
Descendants<Quote> // Text | Paragraph
Descendants<Paragraph> // Text
Descendants<Text> // never
But these both fail:
Descendants<Element>
// Type instantiation is excessively deep and possibly infinite. (2589)
Descendants<Node>
// Type instantiation is excessively deep and possibly infinite. (2589)
Ideally it should return Element | Text.
I understand that TypeScript errors out because it thinks it's recursing infinitely. But is there a way to write this helper such that it works with the generic Element (or Node) types still?
Here is a TypeScript Playground link.