typescript - 如何防止递归条件在 TypeScript 中无限?

标签 typescript

我希望在 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)类型一起使用?

这是一个TypeScript Playground link .

最佳答案

如果添加类型参数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 playground

关于typescript - 如何防止递归条件在 TypeScript 中无限?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66926334/

相关文章:

javascript - 每当输入可观察值发生变化时,如何正确地重新计算 bool 值?

javascript - Angular 4 - 如何根据数量和选项的变化计算价格

node.js - VS Code 不缩进 4 个空格,但仅针对单个文件

typescript - 此条件将始终返回 'false',因为类型 '0' 和 '2' 没有重叠

javascript - Angular2 中的响应式(Reactive)选择

angular2 toastr - 没有 toastr 容器已初始化接收 toast

typescript - 为什么关键属性不被识别为 typescript 中事件类型的一部分

debugging - typescript 的编辑器和调试器

javascript - 如何在文件结构中组织 nodejs 导出但不创建具有相同结构的文件

angular - RxJS 可观察。一旦我有引用,如何重新发射