我希望在 TypeScript 中对节点树进行建模,其中一些是“元素”节点,一些是“文本”节点(如在 DOM 中)。这可以表示为:
type Text = { text: string }
type Element = { children: Node[] }
type Node = Element | Text
然后我尝试编写一个 TypeScript 实用程序,它可以获取树中出现的任何潜在后代节点的类型。我可以这样写:
type Descendants<N extends Node> = N extends { children: Array<infer T> }
? T extends Node
? T | Descendants<T>
: never
: never
给定一组自定义节点:
type Paragraph = { children: Text[] }
type Quote = { children: Paragraph[] }
所有这些工作:
Descendants<Quote> // Text | Paragraph
Descendants<Paragraph> // Text
Descendants<Text> // never
但是这些都失败了:
Descendants<Element>
// Type instantiation is excessively deep and possibly infinite. (2589)
Descendants<Node>
// Type instantiation is excessively deep and possibly infinite. (2589)
理想情况下,它应该返回 Element |文字
.
我知道 TypeScript 出错是因为它认为它在无限递归。但是有没有一种方法可以编写此帮助程序,使其仍然可以与通用 Element
(或 Node
)类型一起使用?
最佳答案
如果添加类型参数Visited
为了表示已访问类型的联合,您可以使用额外的条件来避免遍历这些类型:
type DescendantsPrim<Visited, N extends Node> =
N extends { children: Array<infer T> }
? T extends Node
? T extends Visited
? never
: T | DescendantsPrim<Visited | T, T>
: never
: never
对于Descendents
的定义你可以使用 never
对于 Visited
参数,因为它是 |
的中性元素(即 T | never = T
):
type Descendants<N extends Node> = DescendantsPrim<never, N>
这将推断出以下类型:
type A = Descendants<Quote> // Paragraph | Text
type B = Descendants<Paragraph> // { text: string }
type C = Descendants<Text> // never
type D = Descendants<Element> // Element | Text
type E = Descendants<Node> // Element | Text
关于typescript - 如何防止递归条件在 TypeScript 中无限?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66926334/