如何输入递归可变参数元组

Kyl*_*Mit 5 recursion typescript variadic-tuple-types

我有一个查询元素数组,其中每个元素可以是一个术语或一个子查询,其中包含以“AND”或“OR”开头,后跟一组合法查询元素、术语或嵌套子查询等。

例如,这些都应该是合法的输入:

const query1 = "Hello"
const query2 = ["AND", "Hello", "World"]
const query3 = ["OR", ["AND", "Hello", "World"], ["AND", "Hola", "Mundo"]]
Run Code Online (Sandbox Code Playgroud)

TS 4.0中的Variadric Tuple应该允许您输入数组的第一个元素,并为其余元素输入另一种类型:

type IQuery = ["AND" | "OR", ...Array<string>]
const query = ["AND", "Hello", "World"]  // valid
Run Code Online (Sandbox Code Playgroud)

TS 3.7中的递归类型应该允许您定义使用自身的类型:

type IQueryOps = string | Array<IQueryOps>
const query: IQueryOps = ["Hello", "World", ["Hola", "Mundo"]]  // valid
Run Code Online (Sandbox Code Playgroud)

但当圆形类型展开时,我似乎无法将两者结合起来。在这种情况下,每个查询都以一个运算符开头,后面跟着一个字符串或另一个有效的查询,如下所示:

type IOperator = "AND" | "OR"
type IQuery = [IOperator, ...Array<string | IQuery>]
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我收到错误:

类型别名“IQuery”循环引用自身。(2456)

无论如何,即使有解决方法,也可以输入此内容,还是我必须将其展开到我希望从类型角度支持的所需深度级别?

TS Playground 演示

进一步阅读

jca*_*alz 2

我认为这可能是microsoft/TypeScript#41164中报告的 TypeScript 设计限制的一个实例。正如那里提到的

某些循环是允许的 [...] 但其他循环则不允许,例如

type Identity<T> = T;
type T3 = Identity<T3>;
Run Code Online (Sandbox Code Playgroud)

通用实例化是deferred 的,因此在 TS 分析 [a] 声明时,它无法判断它是在 case 中Record(将被允许)还是在Identitycase 中(将不允许)。只有在这个过程的后期,我们才能知道这实际上是好的,但如果不好,那么回去开始抱怨就“太晚了”。

如果这就是问题所在,那么虽然以下内容应该是“好的”,但编译器似乎无法尽早告知以允许它:

// type IQuery = string | ["AND" | "OR", ...Array<IQuery>] error
Run Code Online (Sandbox Code Playgroud)

(我稍微改变了你的定义以允许这条const query1 = "Hello"线)。


幸运的是,Array<T>有一个替代的内置语法T[],它似乎不会以这种方式延迟:

type IQuery = string | ["AND" | "OR", ...IQuery[]] // okay
Run Code Online (Sandbox Code Playgroud)

这有效:

const query1: IQuery = "Hello"
const query2: IQuery = ["AND", "Hello", "World"]
const query3: IQuery = ["OR", ["AND", "Hello", "World"], ["AND", "Hola", "Mundo"]]
const query4: IQuery = ["AND", ["OR", ["AND"]]]; 

const badQuery1: IQuery = 3; // error
// Type 'number' is not assignable to type 'IQuery'
const badQuery2: IQuery = ["AND", "Hello", 123]; // error
// Type 'number' is not assignable to type 'IQuery'
const badQuery3: IQuery = 
  ["OR", query1, query2, "then", query3, query4, "if", []]; // error
// Type '[]' is not assignable to type 'IQuery'.
Run Code Online (Sandbox Code Playgroud)

Playground 代码链接