2016-05-01 19 views
10

Ich habe den folgenden Code:Wie erstellt man in TypeScript einen zirkulär referenzierten Typ?

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

Typoskript beschwert sich mit dem folgenden Fehler:

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

Offensichtlich Kreis refernces sind nicht erlaubt. Ich brauche diese Art von Struktur jedoch immer noch. Was wäre ein Workaround dafür?

+0

Anscheinend sind zirkuläre Typreferenzen erlaubt: http: // stackoverflow.com/questions/24444436/circular-type-references-in-maschinenschrift – btk

Antwort

7

Hier ist eine Möglichkeit, es zu tun:

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

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

Typen, die sich beziehen, werden als „rekursive Typen“ bekannt und werden in section 3.11.8 der Sprache spec diskutiert. Der folgende Auszug erklärt, warum Ihr Versuch nicht kompiliert:

Classes and interfaces can reference themselves in their internal structure...

Ihr ursprüngliches Beispiel verwendet weder eine Klasse noch eine Schnittstelle; Es verwendet einen Typalias.

+0

Yup, sieht aus wie das wird es sein, danke! – samvv

1

Aufbauend auf NPE sagte, können Typen nicht rekursiv auf sich selbst verweisen, könnten Sie diese Art auf welcher Ebene auch immer der Tiefe entrollen Sie ausreichend angesehen, zum Beispiel:

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

Nicht hübsch, aber beseitigt die Notwendigkeit für eine Schnittstelle oder Klasse mit einem Eigenschaftswert.

+0

In der Tat habe ich daran gedacht, aber leider brauche ich es von unendlicher Tiefe. Danke für die Antwort sowieso. – samvv

10

Wir haben bereits gute Antworten, aber ich denke, dass wir näher kommen können, was Sie in erster Linie wollte:

Sie können so etwas wie dies versuchen:

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 }]]; 

Im Vergleich zu NPE Antwort, Sie Brauchen keine Wrapper-Objekte um Strings und Zahlen.

Wenn Sie eine einzelne Nummer oder Zeichenfolge wollen ein gültiges Dokument sein (das ist nicht das, was Sie gefragt, aber was NPE Antwort impliziert), können Sie dies versuchen:

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:

Die Verwendung einer Schnittstelle mit Indexsignatur anstelle eines Arrays hat den Nachteil, dass Informationen vom Typ verloren gehen. Typescript lässt Sie Array-Methoden wie find, map oder forEach nicht aufrufen. Beispiel:

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); 
} 

Dies kann durch Änderung der Definition von DocumentArray gelöst werden:

interface DocumentArray extends Array<ScalarDocument | DocumentArray> {} 
+0

Interessant ... Danke für das Teilen! – samvv

+0

Sie können auch bestimmte Indizes wie folgt auf einen bestimmten Typ fixieren: Schnittstelle DocumentArray { 0: number; [Index: Nummer]: ScalarDocument | DokumentArray; } – rikkertkoppes

3

Der Schöpfer von Typoskript erklärt, wie rekursive Typen hier erstellen: https://github.com/Microsoft/TypeScript/issues/3496#issuecomment-128553540

Die Abhilfe für die kreisförmige Referenz ist zu verwenden extends Array. In Ihrem Fall würde dies zu dieser Lösung führen:


type Document = [number | string | DocumentArray] 

interface DocumentArray extends Array<Document> { }