我有以下代码:
type Document = [number | string | Array<Document>]
TypeScript抱怨以下错误:
test.ts(7,6): error TS2456: Type alias 'Document' circularly references itself.
显然不允许循环引用。但是,我仍然需要这种结构。对此有什么解决方法?
答案 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