Menu

TypeScript JSON: JSON.parse typisieren, JSON zu Interface, Imports

JSON.parse gibt any zurück, also vertraut TypeScript jedem Typ, den du dem Ergebnis gibst. Wie du geparstes JSON typisierst, mit einem Type Guard validierst, aus einem JSON-Beispiel ein Interface machst, .json-Dateien importierst und was JSON.stringify mit Dates, Sets und undefined macht.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

JSON.parse gibt any zurück, also akzeptiert TypeScript jeden Typ, dem du das Ergebnis zuweist. Das macht das Typisieren von geparstem JSON zu einer Zeile, bedeutet aber auch, dass der Typ ein Versprechen von dir ist und nichts, was der Compiler prüft:

Beim zweiten Objekt ist age der String "41". TypeScript nennt es trotzdem number, weil any allem zugewiesen werden kann, und das Programm gibt 411 aus. Für JSON, das dein eigener Code gerade erst geschrieben hat, ist die Annotation in Ordnung. Daten aus einem Request, einer Datei oder dem Local Storage validierst du.

In unknown parsen

Typisierst du das Ergebnis als unknown, besteht der Compiler auf einer Prüfung, bevor irgendeine Eigenschaft verwendet wird:

Der Fehler lautet index.ts(4,13): error TS18046: 'data' is of type 'unknown'. Jede Prüfung, die du dann schreibst, engt data ein Stück weiter ein.

Mit einem Type Guard validieren

Ein Type Guard ist eine Funktion, die value is User zurückgibt. Liefert sie true, behandelt TypeScript den Wert von da an als User, und die Prüfungen darin sind echte Prüfungen zur Laufzeit:

JSON.parse selbst wirft bei fehlerhaftem Text einen SyntaxError, echter Code packt es also zusätzlich in try/catch. Bei großen oder verschachtelten Daten werden handgeschriebene Guards lang; Schema-Bibliotheken wie Zod oder Valibot lassen dich die Form einmal deklarieren und daraus sowohl den Validator als auch den TypeScript-Typ ableiten.

JSON zu TypeScript-Interface

Ein JSON-Beispiel in Typen umzuwandeln ist Handarbeit nach Schema. Gegeben diese Antwort:

{
    "id": 42,
    "title": "Learn TypeScript",
    "done": false,
    "owner": { "id": 7, "name": "Ada" },
    "tags": ["study", "ts"],
    "dueDate": "2024-03-15T10:30:00Z",
    "notes": null
}

Ordne jedem Wert seinen Typ zu, gib verschachtelten Objekten ein eigenes Interface und markiere, was variieren kann:

Ein einzelnes Beispiel verrät nicht, welche Felder optional oder nullable sind. Sieh dir mehrere Antworten oder die Dokumentation der API an, bevor du dich auf ? und | null festlegst.

Datumswerte und der Reviver

JSON hat keinen Datumstyp, also kommen Datumswerte als Strings an. Das zweite Argument von JSON.parse, der Reviver, wird für jeden Schlüssel aufgerufen und kann sie wiederherstellen:

Der Parameter value des Revivers ist any, ebenso das Ergebnis, der Typ Order wird also weiterhin vertraut statt geprüft. JSON.stringify(order, null, 2) rückt die Ausgabe um zwei Leerzeichen ein und macht aus dem Date wieder seinen ISO-String.

JSON.stringify und was dabei verloren geht

JSON.stringify ist so typisiert, dass es string zurückgibt. Die Werte, die es umwandelt, kommen nicht immer gleich zurück, und der Typ warnt dich nicht:

WertNach JSON.stringify
DateISO-String (über seine Methode toJSON)
Map, Set{} (vorher mit [...set] oder Object.fromEntries(map) umwandeln)
undefined, Funktionen, Symbole in einem Objektder Schlüssel fällt weg
undefined, Funktionen, Symbole in einem Arraynull
undefined, eine Funktion oder ein Symbol alleinundefined, kein String
NaN, Infinitynull
bigintwirft einen TypeError

Die Regeln zur Laufzeit sind dieselben wie in reinem JavaScript, beschrieben unter JSON in JavaScript.

Ein Typ für jeden JSON-Wert

Wenn Code beliebiges JSON verarbeitet, beschreibt ein rekursiver Typ genau, was JSON enthalten kann, und lehnt Werte ab, die es nicht kann:

Ohne den Kommentar ist die letzte Zeile ein Compilerfehler, weil ein Date-Objekt kein JsonValue ist.

.json-Dateien importieren

Eine .json-Datei lässt sich wie ein Modul importieren, und TypeScript leitet ihren Typ aus dem Inhalt ab:

{ "name": "app", "port": 8080, "tags": ["a"] }
// CommonJS output, or a bundler
import config from "./config.json";
const port: number = config.port; // typed from the file: number

// An ES module under module: nodenext
import settings from "./config.json" with { type: "json" };

In TypeScript 7 funktioniert das ohne zusätzliche Einstellungen, wenn module auf nodenext, node20, commonjs, esnext oder preserve steht. Unter node16 und node18 scheitert es mit dem Fehler TS2732, Cannot find module './config.json'. Consider using '--resolveJsonModule' to import module with '.json' extension., bis du "resolveJsonModule": true ergänzt; auf false gesetzt schaltet die Option JSON-Imports überall ab. In einem ES-Modul unter nodenext oder node20 braucht der Import das Attribut with { type: "json" } (ohne gibt es den Fehler TS1543), und nur der Default-Import ist erlaubt (Fehler TS1544 bei import { port }). tsc kopiert die importierte .json-Datei nach outDir, neben das kompilierte JavaScript.

Häufig gestellte Fragen

Welchen Typ gibt JSON.parse in TypeScript zurück?

any. Der Compiler kann nicht wissen, was ein String enthält, also kompiliert const user: User = JSON.parse(text) unabhängig vom Inhalt des Texts. Weise das Ergebnis unknown zu und validiere es, wenn die Daten von außerhalb deines Programms kommen.

Wie wandle ich JSON in ein TypeScript-Interface um?

Nimm ein repräsentatives Beispiel und schreibe pro Schlüssel eine Eigenschaft: Strings, Zahlen und Booleans werden zu string, number und boolean, ein verschachteltes Objekt bekommt ein eigenes Interface, ein Array von Objekten wird zu Item[], und Schlüssel, die manchmal fehlen, bekommen ein ?. Codegeneratoren wie quicktype automatisieren das, aber prüfe ihre Vermutungen an mehr als einem Beispiel.

Wie importiere ich eine JSON-Datei in TypeScript?

import config from "./config.json"; funktioniert in TypeScript 7, wenn module auf nodenext, node20, commonjs, esnext oder preserve steht, und das Ergebnis wird aus dem Inhalt der Datei typisiert. Mit node16 oder node18 setzt du zusätzlich "resolveJsonModule": true. In einem ES-Modul unter nodenext oder node20 ergänzt du das Attribut, das Node verlangt: import config from "./config.json" with { type: "json" };.

Gibt JSON.stringify immer einen String zurück?

Laut Typ string, aber JSON.stringify(undefined) und JSON.stringify(() => 1) geben zur Laufzeit undefined zurück. Auch Werte innerhalb von Objekten werden umgewandelt: Ein Date wird zu einem ISO-String, und Map und Set werden zu {}.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S