如何在TypeScript中创建循环引用类型?

时间:2016-05-01 12:05:36

标签: types typescript circular-reference

我有以下代码:

type Document = [number | string | Array<Document>]

TypeScript抱怨以下错误:

test.ts(7,6): error TS2456: Type alias 'Document' circularly references itself.

显然不允许循环引用。但是,我仍然需要这种结构。对此有什么解决方法?

5 个答案:

答案 0 :(得分:16)

我们已经有了很好的答案,但我认为我们可以从一开始就接近你想要的东西:

您可以尝试这样的事情:

interface Document {
    [index: number]: number | string | Document;
}

// compiles
const doc1: Document = [1, "one", [2, "two", [3, "three"]]];

// fails with "Index signatures are incompatible" which probably is what you want
const doc2: Document = [1, "one", [2, "two", { "three": 3 }]];

与NPE的答案相比,您不需要围绕字符串和数字的包装器对象。

如果您希望单个数字或字符串成为有效文档(这不是您提出的问题,而是NPE的答案所暗示的内容),您可以尝试这样做:

type ScalarDocument = number | string;
interface DocumentArray {
    [index: number]: ScalarDocument | DocumentArray;
}
type Document = ScalarDocument | DocumentArray;

const doc1: Document = 1;
const doc2: Document = "one";
const doc3: Document = [ doc1, doc2 ];

<强>更新

使用带索引签名的接口而不是数组具有丢失类型信息的缺点。 Typescript不允许您调用find,map或forEach等数组方法。例如:

type ScalarDocument = number | string;
interface DocumentArray {
    [index: number]: ScalarDocument | DocumentArray;
}
type Document = ScalarDocument | DocumentArray;

const doc1: Document = 1;
const doc2: Document = "one";
const doc3: Document = [ doc1, doc2 ];
const doc = Math.random() < 0.5 ? doc1 : (Math.random() < 0.5 ? doc2 : doc3);

if (typeof doc === "number") {
    doc - 1;
} else if (typeof doc === "string") {
    doc.toUpperCase();
} else {
    // fails with "Property 'map' does not exist on type 'DocumentArray'"
    doc.map(d => d);
}

这可以通过更改DocumentArray的定义来解决:

interface DocumentArray extends Array<ScalarDocument | DocumentArray> {}

答案 1 :(得分:15)

TypeScript的创建者解释了如何在此创建递归类型:https://github.com/Microsoft/TypeScript/issues/3496#issuecomment-128553540

循环引用的解决方法是使用extends Array。在你的情况下,这将导致这个解决方案:


type Document = [number | string | DocumentArray]

interface DocumentArray extends Array<Document> { }

答案 2 :(得分:12)

这是一种方法:

class Doc {
  val: number | string | Doc[];
}

let doc1: Doc = { val: 42 };
let doc2: Doc = { val: "the answer" };
let doc3: Doc = { val: [doc1, doc2] };

引用自身的类型称为“递归类型”,并在语言规范的section 3.11.8中进行了讨论。以下摘录解释了为什么您的尝试无法编译:

  

类和接口可以在其内部结构中引用自己......

您的原始示例既不使用类也不使用接口;它使用类型别名

答案 3 :(得分:1)

基于NPE所说的,类型不能递归地指向自己,你可以将这种类型展开到你认为足够的深度,例如:

type Document = [number|string|[number|string|[number|string|[number|string]]]]

不漂亮,但不需要具有属性值的接口或类。

答案 4 :(得分:0)


type Circular = {
    infinity: Map<string, Circular>
}

type Store = Circular['infinity']

declare var map:Store;

const foo = map.get('sd') // Circular | undefined