Jeder TypeScript-Entwickler kennt diese Situation: Sie integrieren eine neue API, haben eine Beispielantwort und brauchen typisierte Interfaces, bevor Sie eine einzige Zeile Geschäftslogik schreiben können. Diese Interfaces von Hand zu schreiben ist mühsam, repetitiv und fehleranfällig — vor allem, wenn die Antwort tief verschachtelt ist oder Dutzende Felder hat. Dieser Leitfaden behandelt jeden praktischen Ansatz, JSON in TypeScript-Interfaces zu überführen: wie die Ausgabe aussehen sollte, wie Sie sie automatisch erzeugen und wie Sie kniffelige Fälle behandeln.
Warum TypeScript-Interfaces bei JSON-Daten wichtig sind
Wenn Sie in TypeScript eine API ohne Typannotationen aufrufen, ist die geparste Antwort any. Das heißt: keine Autovervollständigung, keine Typprüfung und keine Sicherheit zur Compile-Zeit. Ein Tippfehler in einem Eigenschaftsnamen wird zu einem Laufzeitfehler statt zu einem Build-Fehler.
// Ohne Typen — alles ist any
const response = await fetch('/api/users/42');
const user = await response.json(); // user: any
console.log(user.naem); // Tippfehler — kein Fehler zur Compile-Zeit
// Mit typisiertem Interface
interface User {
id: number;
name: string;
email: string;
}
const response = await fetch('/api/users/42');
const user: User = await response.json();
console.log(user.naem); // Error: Property 'naem' does not exist on type 'User'Das Interface verursacht null Laufzeitkosten — es existiert nur zur Compile-Zeit. Aber es verändert das Entwicklungserlebnis komplett.
Was eine JSON-zu-TypeScript-Konvertierung erzeugt
Jedes JSON-Objekt wird zu einem TypeScript-interface, jede JSON-Primitive bildet auf ihr TypeScript-Äquivalent ab, und Arrays leiten ihren Elementtyp ab.
// JSON-Eingabe
{
"id": 42,
"name": "Alice Johnson",
"email": "alice@example.com",
"active": true,
"score": 98.5,
"address": {
"street": "123 Main St",
"city": "New York",
"zip": "10001"
},
"tags": ["admin", "editor"],
"orders": [
{ "id": 101, "total": 49.99, "status": "shipped" },
{ "id": 102, "total": 99.00, "status": "pending" }
]
}// TypeScript-Ausgabe
interface Root {
id: number;
name: string;
email: string;
active: boolean;
score: number;
address: Address;
tags: string[];
orders: Order[];
}
interface Address {
street: string;
city: string;
zip: string;
}
interface Order {
id: number;
total: number;
status: string;
}Jedes verschachtelte Objekt wird zu einem benannten Interface. Der Name kommt vom übergeordneten Schlüssel — address wird zu Address, Elemente von orders werden zu Order. Das hält die Typen lesbar und komponierbar.
Die Regeln des Typ-Mappings
Wenn Sie verstehen, wie JSON-Typen auf TypeScript-Typen abbilden, wird die Ausgabe vorhersehbar und Sie können sie zuversichtlich anpassen.
| JSON-Wert | TypeScript-Typ |
|---|---|
"hello" | string |
42, 3.14 | number |
true, false | boolean |
null | null |
{} | benanntes interface |
['a', 'b'] | string[] |
[1, 'two'] | (number | string)[] |
[] (leer) | unknown[] |
Eine Feinheit: TypeScript hat keinen Integer-Typ. Sowohl 42 als auch 3.14 werden zu number. Wenn die Unterscheidung für Ihren Anwendungsfall wichtig ist, können Sie Branded Types oder einen Kommentar verwenden.
Methode 1: Einen Online JSON-zu-TypeScript-Konverter nutzen
Für einmalige Aufgaben — ein neuer API-Endpunkt, ein schneller Prototyp — ist ein Online-Tool der schnellste Weg. fixjsons JSON-zu-TypeScript-Konverter erledigt die gesamte Konvertierung in Ihrem Browser:
- Ihr JSON einfügen in das Eingabefeld. Das Tool akzeptiert gültiges JSON oder kaputtes JSON mit gängigen Problemen wie nachgestellten Kommas und einfachen Anführungszeichen — es repariert das JSON vor der Konvertierung.
- TypeScript-Interfaces erscheinen sofort auf der rechten Seite — kein Klick nötig.
- Ausgabe kopieren und direkt in Ihre TypeScript-Datei einfügen.
Alles läuft lokal. Keine Daten werden an einen Server gesendet — wichtig, wenn das verarbeitete JSON API-Schlüssel, Nutzerdaten oder interne Konfiguration enthält.
Methode 2: Die Konvertierung in TypeScript selbst schreiben
Wenn Sie Typen programmatisch erzeugen müssen — als Teil eines Build-Skripts oder Codegenerators — können Sie die Logik selbst schreiben. Hier eine minimale, aber vollständige Implementierung:
function jsonToTypeScript(value: unknown, rootName = 'Root'): string {
const interfaces: string[] = [];
const seen = new Set<string>();
function cap(s: string) {
return s ? s.charAt(0).toUpperCase() + s.slice(1) : 'Unknown';
}
function singular(name: string) {
if (/ies$/i.test(name)) return name.slice(0, -3) + 'y';
if (/[^s]s$/i.test(name)) return name.slice(0, -1);
return name + 'Item';
}
function getType(val: unknown, name: string): string {
if (val === null) return 'null';
if (typeof val === 'boolean') return 'boolean';
if (typeof val === 'number') return 'number';
if (typeof val === 'string') return 'string';
if (Array.isArray(val)) {
if (val.length === 0) return 'unknown[]';
const objs = val.filter(
(v): v is Record<string, unknown> =>
v !== null && typeof v === 'object' && !Array.isArray(v),
);
if (objs.length > 0) {
const itemName = cap(singular(name));
buildInterface(itemName, objs);
return `${itemName}[]`;
}
const types = [...new Set(val.map((v) => getType(v, name)))];
return types.length === 1 ? `${types[0]}[]` : `(${types.join(' | ')})[]`;
}
if (typeof val === 'object') {
const iName = cap(name);
buildInterface(iName, [val as Record<string, unknown>]);
return iName;
}
return 'unknown';
}
function buildInterface(name: string, objs: Record<string, unknown>[]): void {
if (seen.has(name)) return;
seen.add(name);
const allKeys = [...new Set(objs.flatMap((o) => Object.keys(o)))];
const props: string[] = [];
for (const key of allKeys) {
const present = objs.filter((o) => key in o);
const optional = present.length < objs.length ? '?' : '';
const propType = getType(present[0][key], cap(key));
const safeKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : `"${key}"`;
props.push(` ${safeKey}${optional}: ${propType};`);
}
interfaces.push(`interface ${name} {\n${props.join('\n')}\n}`);
}
const rootType = getType(value, rootName);
const defs = interfaces.reverse().join('\n\n');
if (rootType !== cap(rootName)) {
return `type ${cap(rootName)} = ${rootType};\n\n${defs}`.trim();
}
return defs;
}Verwendung in einem Node.js-Build-Skript:
import * as fs from 'fs';
const json = JSON.parse(fs.readFileSync('api-response.json', 'utf8'));
const typescript = jsonToTypeScript(json, 'UserResponse');
fs.writeFileSync('types/user.ts', typescript);Methode 3: quicktype (CLI und Bibliothek)
quicktype ist das umfangreichste Open-Source-Werkzeug, um aus JSON TypeScript-Typen zu generieren. Es behandelt Sonderfälle, die einfache Generatoren übersehen, und unterstützt mehrere Ausgabeformate.
npm install -g quicktype
# Aus einer lokalen Datei
quicktype api-response.json -o types/user.ts --lang typescript
# Aus einer URL
quicktype https://api.example.com/users/42 -o types/user.ts --lang typescript
# Aus stdin
cat api-response.json | quicktype --lang typescriptquicktype erzeugt auch Laufzeit-Validierungsfunktionen mit Bibliotheken wie io-ts oder zod — hilfreich, wenn Sie eingehende Daten validieren wollen, nicht nur statisch typisieren.
Schwierige Fälle behandeln
Nullable Felder
JSON-APIs liefern oft null für fehlende Werte. Ein Generator, der null sieht, erzeugt null als Typ — was Sie meist wollen, ist aber string | null:
// Generiert (erster Wurf — zu eng)
interface Root {
name: string;
middleName: null;
}
// Was Sie schreiben sollten
interface Root {
name: string;
middleName: string | null;
}Prüfen Sie nullable Felder nach der Generierung immer manuell. Wenn Sie mehrere Beispielantworten haben, in denen ein Feld mal string und mal null ist, folgert ein guter Generator automatisch string | null.
Optional vs. nullable
TypeScript unterscheidet zwischen field?: string (Schlüssel kann fehlen) und field: string | null (Schlüssel immer vorhanden, Wert darf null sein):
interface Order {
id: number;
total: number;
discount?: number; // Schlüssel fehlt in manchen Antworten
note: string | null; // Schlüssel immer da, Wert darf null sein
}Generatoren, die mehrere JSON-Samples analysieren, leiten diesen Unterschied korrekt ab. Single-Sample-Generatoren markieren fehlende Schlüssel als optional, können aber einen fehlenden Schlüssel nicht von einem Null-Wert unterscheiden.
Schlüssel, die keine gültigen Bezeichner sind
Manche JSON-Schlüssel enthalten Zeichen, die als TypeScript-Eigenschaftsname nicht erlaubt sind:
// JSON
{ "content-type": "application/json", "x-request-id": "abc123" }
// Generiertes TypeScript — gültig, erfordert aber Klammerzugriff
interface Root {
"content-type": string;
"x-request-id": string;
}
// Zugriff per Klammer-Notation
obj["content-type"]Wenn Sie die API kontrollieren, bevorzugen Sie camelCase-Schlüssel, um das ganz zu vermeiden. Andernfalls ist die Schreibweise mit Anführungszeichen gültiges TypeScript und funktioniert korrekt.
Arrays gemischter Typen
// JSON
{ "values": [1, "two", true, null] }
// Generiert
interface Root {
values: (number | string | boolean | null)[];
}Solche Union-Typen sind technisch korrekt, signalisieren aber meist ein Designproblem in der API. Prüfen Sie, ob das Feld tatsächlich gemischt ist oder die Beispieldaten irreführend sind.
Generierte Typen sicher verwenden
Ein Interface zu generieren ist nur der erste Schritt. Das Interface ist ein Compile-Zeit-Vertrag — es validiert zur Laufzeit nichts. response.json() liefert any, und TypeScript lässt Sie es ohne echte Prüfung auf Ihr Interface casten.
Für interne APIs, bei denen Sie beide Seiten kontrollieren, reicht das meist. Für externe APIs oder benutzerseitige Daten validieren Sie zur Laufzeit. Empfehlenswert ist Zod:
import { z } from 'zod';
const UserSchema = z.object({
id: z.number(),
name: z.string(),
email: z.string().email(),
active: z.boolean(),
address: z.object({
street: z.string(),
city: z.string(),
zip: z.string(),
}),
tags: z.array(z.string()),
orders: z.array(z.object({
id: z.number(),
total: z.number(),
status: z.string(),
})),
});
type User = z.infer<typeof UserSchema>; // TypeScript-Typ aus dem Schema abgeleitet
const raw = await response.json();
const user = UserSchema.parse(raw); // wirft, wenn die Form nicht passtZods Ansatz vereint beide Vorteile: das Schema ist die einzige Quelle der Wahrheit für TypeScript-Typ und Laufzeitvalidierung. Wenn sich die API breaking ändert, merken Sie es genau dort, wo die Daten in Ihr System gelangen.
Über Interfaces hinaus: Validatoren und typisierte Clients
Ein generiertes Interface ist ein Compile-Zeit-Vertrag. Eine Stufe darüber stehen Bibliotheken, die Ihnen aus einer Quelle Typ und Laufzeitprüfung liefern:
- Zod und io-ts — Schema definieren, daraus den TypeScript-Typ ableiten und eingehende Daten an der Grenze validieren. Wenn sich die API ändert, sagt Ihnen Ihr Build (und Ihr Error-Log) das sofort.
- tRPC und typisierte
fetch-Wrapper (z. B.openapi-fetch) — die serverseitige Prozedur oder die OpenAPI-Definition ist die Quelle der Wahrheit, der Client bekommt automatisch passende Typen, ohne JSON-zu-TS-Schritt dazwischen. - ts-pattern und diskriminierte Unions — sobald Ihre Daten ein Tag-Feld besitzen (z. B.
{ kind: "circle" | "square", ... }), erkennt erschöpfendes Pattern-Matching vergessene Fälle zur Compile-Zeit.
Typen mit der API synchron halten
Generierte Typen haben ein Verfallsdatum. APIs ändern sich — ein Feld wird umbenannt, ein neues Pflichtfeld kommt hinzu oder ein String-Feld wird nullable. Ihre Interfaces veralten still.
Aus der OpenAPI-Spec generieren, nicht aus Beispielantworten
Wenn die API eine OpenAPI- (Swagger-)Definition hat, erzeugt openapi-typescript Typen direkt aus der Spec:
npm install -g openapi-typescript
openapi-typescript https://api.example.com/openapi.json -o types/api.tsDas ist viel zuverlässiger als aus einer Beispielantwort zu generieren, da die Spec jedes Feld dokumentiert — inklusive optionaler, nullable Varianten und Enum-Werte.
Typen in der CI neu generieren
Fügen Sie eine CI-Stufe hinzu, die Typen aus der Spec oder einem bekannten guten Sample neu generiert und dann tsc --noEmit ausführt, um Brüche zu erkennen. Wenn sich die API ändert, schlägt der Build fehl, bevor die breaking change in die Produktion gelangt.
Häufig gestellte Fragen
Wie wandle ich JSON in ein TypeScript-Interface um?
JSON in einen Online-Konverter wie fixjsons JSON-zu-TypeScript-Tool einfügen für sofortige Ausgabe, quicktype in einem Build-Schritt laufen lassen oder mit openapi-typescript aus einer OpenAPI-Spec generieren — für langlebigen Code.
Wie bilden JSON-Typen auf TypeScript-Typen ab?
Strings → string, alle Zahlen → number (TypeScript hat keinen Integer-Typ), Booleans → boolean, Objekte → benannte Interfaces, und Arrays leiten ihren Elementtyp ab. Siehe Mapping-Tabelle oben und die sechs JSON-Datentypen.
Wie behandle ich nullable oder optionale Felder?
Nutzen Sie field?: T, wenn der Schlüssel fehlen kann, und field: T | null, wenn der Schlüssel immer da ist, der Wert aber null sein kann. Generatoren, die mehrere Samples analysieren, leiten dies ab; Single-Sample-Tools brauchen manuelle Nachsicht.
Validieren TypeScript-Interfaces JSON zur Laufzeit?
Nein — Interfaces werden zur Compile-Zeit gelöscht. Für Laufzeitsicherheit bei externen Daten validieren Sie mit Zod oder beschreiben die Form mit JSON Schema.
Fazit
JSON in TypeScript-Interfaces umzuwandeln ist etwas, das jeder TypeScript-Entwickler regelmäßig tut. Der richtige Ansatz hängt vom Kontext ab: Für eine schnelle einmalige Integration bringt Sie ein Online-Konverter in unter einer Minute zum Laufen. Für eine Build-Pipeline automatisiert quicktype oder ein eigenes Skript den Prozess. Für langlebigen Produktionscode liefert das Generieren aus einer OpenAPI-Spec plus Laufzeitvalidierung mit Zod die stärksten Garantien.
Welche Methode auch immer Sie nutzen — das generierte Interface ist ein Ausgangspunkt. Prüfen Sie nullable Felder, fügen Sie Kommentare für nicht offensichtliche Eigenschaften hinzu und straffen Sie Bedingungen dort, wo Ihre Geschäftsregeln strenger sind als das, was das JSON-Sample nahelegt. Typen, die Ihre Daten präzise abbilden, machen die gesamte Codebasis wartbarer.
Wenn das JSON schon vor dem Versuch der Konvertierung Syntaxfehler hat, reparieren Sie es zuerst. Ein TypeScript-Generator — wie jeder JSON-Parser — verlangt syntaktisch gültige Eingabe.