如何在 TypeScript 中创建循环引用类型?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/36966444/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to create a circularly referenced type in TypeScript?
提问by samvv
I have the following code:
我有以下代码:
type Document = number | string | Array<Document>;
TypeScript complains with the following error:
TypeScript 抱怨以下错误:
test.ts(7,6): error TS2456: Type alias 'Document' circularly references itself.
Clearly circular references are not allowed. However, I still need this kind of structure. What would be a workaround for this?
显然不允许循环引用。但是,我仍然需要这种结构。什么是解决方法?
采纳答案by Kristian Hanekamp
The creator of TypeScript explains how to create recursive types here.
TypeScript 的创建者在这里解释了如何创建递归类型。
The workaround for the circular reference is to use extends Array
. In your case this would lead to this solution:
循环引用的解决方法是使用extends Array
. 在您的情况下,这将导致此解决方案:
type Document = number | string | DocumentArray;
interface DocumentArray extends Array<Document> { }
Update (TypeScript 3.7)
更新(打字稿 3.7)
Starting with TypeScript 3.7, recursive type aliases will be permitted and the workaround will no longer be needed. See: https://github.com/microsoft/TypeScript/pull/33050
从 TypeScript 3.7 开始,将允许使用递归类型别名,并且不再需要解决方法。请参阅:https: //github.com/microsoft/TypeScript/pull/33050
回答by Daniel Faber
We already have good answers, but I think we can get closer to what you wanted in the first place:
我们已经有了很好的答案,但我认为我们可以更接近你想要的东西:
You may try something like this:
你可以尝试这样的事情:
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 }]];
Compared to NPE's answer, you don't need wrapper objects around strings and numbers.
与 NPE 的答案相比,您不需要围绕字符串和数字的包装对象。
If you want a single number or string to be a valid document (which is not what you asked, but what NPE's answer implies), you may try this:
如果您希望单个数字或字符串成为有效文档(这不是您所问的,而是 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 ];
Update:
更新:
Using an interface with index signature instead of an array has the disadvantage of losing type information. Typescript won't let you call array methods like find, map or forEach. Example:
使用带有索引签名的接口而不是数组的缺点是会丢失类型信息。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);
}
This can be solved by changing the definition of DocumentArray:
这可以通过改变 DocumentArray 的定义来解决:
interface DocumentArray extends Array<ScalarDocument | DocumentArray> {}
回答by NPE
Here is one way to do it:
这是一种方法:
class Doc {
val: number | string | Doc[];
}
let doc1: Doc = { val: 42 };
let doc2: Doc = { val: "the answer" };
let doc3: Doc = { val: [doc1, doc2] };
Types that reference themselves are known as "recursive types" and are discussed in section 3.11.8of the language spec. The following excerpt explains why your attempt does not compile:
引用自身的类型称为“递归类型”,并在语言规范的第 3.11.8 节中讨论。以下摘录解释了您的尝试无法编译的原因:
Classes and interfacescan reference themselves in their internal structure...
类和接口可以在它们的内部结构中引用它们自己...
Your original example uses neither a class nor an interface; it uses a type alias.
您的原始示例既不使用类也不使用接口;它使用类型别名。
回答by ArcSine
Building on what NPE said, types cannot recursively point to themselves, you could unroll this type to whatever level of depth you considered sufficient, e.g.:
根据 NPE 所说,类型不能递归地指向自己,您可以将此类型展开到您认为足够的任何深度级别,例如:
type Document = [number|string|[number|string|[number|string|[number|string]]]]
Not pretty, but removes the need for an interface or class with a property value.
不漂亮,但不需要具有属性值的接口或类。