为什么 TypeScript 递归类型别名因泛型而失败

Why does TypeScript recursive type alias fail with generics

我有两个描述几乎相同事物的类型定义:

 // compilation fails with `Type alias 'StringOrRecordOfStrings' circularly references itself.ts(2456)`
type StringOrRecordOfStrings = string | string[] | Record<string, StringOrRecordOfStrings>;

 // compiles without problems
type StringOrRecordOfStrings = string | string[] | { [property: string]: StringOrRecordOfStrings };

有谁能解释为什么第一个类型定义不能编译?

不允许 Record<string, StringOrRecordOfStrings> 的原因是 Record 是通用 类型 ,而不是 class 或接口。

这方面没有很多明确的文档,但是对象中的属性、索引签名和映射类型的递归类型引用已经存在了很长一段时间。以下内容早在 TypeScript 3.3 就可以使用:

type Recursive = {p: Recursive} | {[p: string]: Recursive} | {[Key in 'a']: Recursive}

TypeScript 3.3 playground

这就是检查第二个示例类型(带有索引签名)的原因。

TypeScript 3.7 扩展了对递归引用的支持,如 PR:

中所述
  • 泛型实例化 class 和接口类型(例如 Array<Foo>)。
  • 数组类型(例如Foo[])。
  • 元组类型(例如 [string, Foo?])。

所以现在,这三个例子也是有效的:

type RecursiveCI = Promise<RecursiveCI>
type RecursiveT = [number, boolean, RecursiveT]
type RecursiveA = RecursiveA[]

我假设该示例只是测试代码,但您可以使用像这样的辅助界面对其进行类型检查:

type StringOrRecordOfStrings = string | string[] | Record<string, RecordInterface>

interface RecordInterface extends Record<string, StringOrRecordOfStrings> {}

TypeScript playground