JSON do TypeScriptu: rozhraní, typové strážce a limity generování
Shrnutí: Vygenerovat rozhraní ze vzorku JSON je jen polovina práce: volitelná pole, null, sjednocení, data a velká celá čísla potřebují lidský zásah a typy neprovádějí žádnou validaci za běhu.

Stručně: rozhraní vygenerujte, abyste ušetřili psaní, poté ručně zkontrolujte volitelnost a null, data a velká celá čísla ponechte jako řetězce a na hranici přidejte validaci za běhu; pokud vygenerované typy považujete za smlouvu, nemáte žádnou validaci.
Generátor vidí jen jeden vzorek, který do něj vložíte. Zda pole mohou chybět, zda hodnoty mohou být null a jaký formát data používají, jsou všechno hádanky – a špatné hádanky musíte opravit vy.
1. Co generátor vlastně dělá
Vložte vzorek JSON do JSON do TypeScriptu a vrácené rozhraní je shrnutím tvaru: spočítá klíče a typy hodnot, které vidělo, a poté zvolí nejužší dostupnou reprezentaci.
{
"id": 1,
"email": "ada@example.com",
"profile": { "nickname": "Ada", "age": 36 },
"tags": ["admin", "beta"],
"createdAt": "2026-10-31T08:00:00Z"
}
Výstup vypadá zhruba takto:
interface User {
id: number;
email: string;
profile: { nickname: string; age: number };
tags: string[];
createdAt: string;
}
Zkompiluje se to, ale popisuje ideální hodnotu, nikoli skutečnou smlouvu endpointu. Generátor neví, zda email může chybět, zda tags může být prázdné, nebo zda createdAt vždy nese časové pásmo. Neví dokonce ani, že id může v jiných záznamech být řetězec.
Správné očekávání tedy je: generátor ušetří psaní, význam dodá člověk.
2. Volitelná pole a null: ? není | null
Toto je část vygenereného výstupu, která se kopíruje nejčastěji, a část, která se nejčastěji láme.
| Forma v TypeScriptu | Význam | Odpovídající JSON |
|---|---|---|
email: string |
Vždy přítomné, vždy řetězec | {"email":"a@b.c"} |
email?: string |
Klíč může chybět; když je přítomen, nemůže být null | {} nebo {"email":"a@b.c"} |
email: string | null |
Klíč je vždy přítomen, hodnota může být null | {"email":null} |
email?: string | null |
Klíč může chybět a hodnota může být null | Všechny tři případy |
Generátor viděl jeden vzorek, takže obvykle vypíše první formu. Pokud API skutečně může vrátit null, typ lže: všechno se zkompiluje a poté email.toLowerCase() za běhu vyhodí chybu.
Opravit to lze dvěma způsoby:
- Sesbírejte několik skutečných vzorků pokrývajících „klíč chybí“ i „hodnota je null“ a nechte generátor odvozovat znovu.
- Po vygenerování ručně změňte pole na
?: T | nulla v kódu skutečně ošetřete obě větve.
Neberte zkratku a neoznačujte vše jako ?: string. Volitelné znamená „klíč nemusí existovat“, nikoli „hodnota může být prázdná“. undefined a null se také chovají v JSON.stringify odlišně: první se vynechá, druhé se ponechá jako null.
3. Nerovnoměrné prvky pole a sjednocené typy
Pole jsou struktura, kterou nejsnáze pokazíte. Pokud se tags vyskytuje ve třech záznamech jako ["admin"], [] a ["beta", 3], generátor může vypsat string[], (string | number)[] nebo unknown[] podle toho, které vzorky viděl.
Člověk musí rozhodnout:
- Typy prvků jsou skutečně smíšené: napište
(string | number)[]a na místě použití zužte. - Špinavý je jen jeden záznam: opravte data. Neskrývejte to pomocí
any[]. - Prázdná pole: generátor nemůže odvodit typ prvku a vrátí se k
never[]neboany[], takže ho musíte doplnit. - N-tice pevné délky: použijte
[string, string]jen tehdy, když obchod skutečně zaručuje délku; jinak se přidání prvku později stane chybou kompilace.
Pole objektů si také zaslouží pozornost, když jsou klíče nerovnoměrné:
interface OrderItem {
sku: string;
quantity: number;
discount?: number; // některé záznamy toto pole nemají
}
Volitelnost dejte na typ prvku, nikoli na pole. items?: OrderItem[] znamená, že pole samotné může chybět, což je zcela jiné tvrzení.
4. Data, velká celá čísla, výčty: typy, které JSON nemá
JSON má jen řetězce a čísla; obchodní význam žije v konvencích. Generátor odvozuje z literálů, takže ho ručně opravte:
| Literál JSON | Obvyklý vygenerovaný výstup | Co by to mělo být |
|---|---|---|
"2026-10-31T08:00:00Z" |
string |
Ponechte string v přenosové vrstvě a převeďte na místě použití; nikdy nedeklarujte přímo Date |
1717075200000 |
number |
Pro časové razítko v milisekundách použijte number a zdokumentujte jednotku |
"1234567890123456789" |
string |
Ponechte string, aby number nemohl ztratit přesnost |
1 a "1" smíšené |
number | string |
Normalizujte na jednu formu; netlačte sjednocení do obchodního kódu |
"admin" |
string |
Literálové sjednocení 'admin' | 'editor', nebo sdílená konstanta výčtu |
true a "true" smíšené |
boolean | string |
Opravte zdroj dat, místo abyste generovali tento typ |
Nejdůležitější pravidlo: data v JSON jsou vždy řetězce. Zápis createdAt: Date je častý sebeklam – JSON.parse nikdy nevytvoří Date, takže hodnota za běhu je stále řetězec a .getTime() vyhodí chybu. Ponechte přenosový typ jako string, na hranici explicitně zavolejte new Date(value) a zkontrolujte Number.isNaN.
Velká celá čísla fungují stejně. Jakmile id přesáhne 2^53, JSON.parse už přesnost ztratil a generování number jen upevní chybu v typu. Backend vrací řetězec, frontend ho ponechá jako řetězec; to je jediná spolehlivá volba.
5. Vygenerované typy neprovádějí validaci za běhu
Typy TypeScriptu se při kompilaci vymažou. Ať je rozhraní sebepřesnější, nemůže zabránit odpovědi {"id": "x"} – omezuje váš vlastní kód, nikoli bajty přicházející po síti.
/**
* Zkontroluje, zda je hodnota za běhu objekt uživatele.
* @param value Hodnota za běhu k validaci
* @returns Zda splňuje minimální tvar User
*/
function isUser(value: unknown): value is User {
if (typeof value !== 'object' || value === null) {
return false;
}
const candidate = value as Record<string, unknown>;
return typeof candidate.id === 'number' && typeof candidate.email === 'string';
}
Typový strážce pokrývá jen pole, která zapíšete do kontroly. S přibývajícími poli se ručně psaní strážci stávají druhou definici k údržbě, která se vzdaluje od rozhraní.
Praktický kompromis vypadá takto:
| Přístup | Validace za běhu | Zdroj typů | Hodí se pro |
|---|---|---|---|
| Jen rozhraní | Žádná | Ručně psané nebo vygenerované | Důvěryhodná interní data, omezení při kompilaci |
| Rozhraní plus ruční strážce | Ano | Dvě definice, které se rozcházejí | Málo polí, jasná hranice |
| zod / valibot | Ano | Typy odvozené ze schématu | Externí API, formuláře, konfigurace |
| JSON Schema plus validátor | Ano | Typy generované ze schématu | Kontrakty napříč jazyky, dokumentace |
Pokud data přicházejí zvenčí, udělejte schéma jediným zdrojem pravdy a odvoďte typy pomocí z.infer nebo json-schema-to-ts. Pak jsou „validace prošla“ a „typ je správný“ stejná skutečnost a neexistuje druhá definice, která by se rozcházela. Úplnému obousměrnému přístupu se věnuje Synchronizace schématu a typů TypeScript.
6. Interface nebo type: nehádejte se o vygenerený výstup
Generátory obvykle vypisují interface, ale oba konstrukty jsou ve většině situací vzájemně zastupitelné. Skutečně záleží na třech schopnostech:
- Sloučení deklarací:
interfacelze deklarovat vícekrát a automaticky se sloučí;typene. Hodí se při záplatování typů třetích stran, ale obchodní modely by se tomu měly vyhýbat – sloučení nechá „nadbytečná pole“ proklouznout kompilátorem bez povšimnutí. - Sjednocení a mapované typy:
typeumí přímo vyjádřitA | B,Partial<T>a typy šablonových literálů;interfacene. - Rekurze: oba ji podporují, ale složité rekurzivní tvary se čtou přirozeněji jako
type.
Užitečnější týmové pravidlo: pro tvary objektů používejte interface, pro sjednocení, pomocné typy a typy funkcí type. Vygenerený výstup nechte ve výchozím stavu a čas při revizi věnujte volitelnosti a validaci za běhu.
7. Nástrojový řetězec: tři způsoby generování
| Přístup | Vstup | Přednost | Omezení |
|---|---|---|---|
| JSON do TypeScriptu na tomto webu | Vzorek JSON | Funguje okamžitě, zpracování lokálně | Jen podle aktuálního vzorku; vyžaduje ruční zúžení |
| quicktype | JSON / JSON Schema | Vícejazyčný výstup, integrace do CLI | Konzervativní odvozování, sjednocení mohou být příliš široká |
| json-schema-to-typescript | JSON Schema | Řízené kontraktem a reprodukovatelné | Vyžaduje nejprve důvěryhodné schéma |
Typické volání quicktype:
npx quicktype -s json -o user.ts user.json --just-types
--just-types vypíše pouze typy a žádný kód pro převod za běhu, což vyhovuje projektům, které už mají parsovací vrstvu. Pokud je schéma ručně psané nebo ho produkuje backend, json-schema-to-typescript se hodí víc, protože respektuje required, enum a additionalProperties – omezení, která odvozování ze vzorku prostě nevidí.
Generování schématu má vlastní úskalí, například označení všech polí jako povinných nebo tiché převádění null na volitelná pole; viz častá úskalí generátoru JSON Schema.
8. Kontrolní seznam před commitem vygenerovaných typů
Než vygenerený výstup committed, projděte těchto pět bodů:
- Normalizujte názvy polí: nenechte
userIdauser_idkoexistovat; zmapujte je jednou v převodní vrstvě. - Zkontrolujte volitelnost: porovnejte se skutečnými odpověďmi a najděte pole, která mohou chybět nebo být
null. - Ponechte data a id jako řetězce: zdokumentujte jednotku a formát v typu a na místě použití parsujte explicitně.
- Snižte
anyaunknown: kdekoli generátor vypsalany, buď přidejte typ, nebo ponechteunknowna přidejte strážce. - Přidejte validaci za běhu: externí data validujte alespoň jednou; nečekejte, že rozhraní špatná data zachytí.
Pokud projekt používá několik JSON knihoven (JSON.parse, superjson, lossless-json), jejich chování u velkých celých čísel a dat se liší, takže to potvrďte už při výběru. Viz Výběr knihovny pro parsování JSON.
Proč generátor označí každé volitelné pole jako povinné?
Viděl jediný vzorek, takže každý klíč, který se objevil, považuje za vždy přítomný. Poskytněte více vzorků pokrývajících různé případy, nebo generujte z JSON Schema – pole required je tam autoritativním zdrojem.
Proč deklarace Date v rozhraní za běhu selhává?
JSON.parse produkuje jen řetězce, čísla, booleany, null, objekty a pole; nikdy nevytvoří Date. Deklarace Date je slib při kompilaci, zatímco hodnota za běhu zůstává řetězec. Ponechte string v přenosové vrstvě a převeďte na hranici.
Lze vygenerované typy použít jako validaci?
Ne. Typy TypeScriptu se po kompilaci vymažou a na data za běhu nekladou žádné požadavky. Externí data validujte pomocí zod, valibot, ajv nebo ručně psaného typového strážce.
Má být id s velkým celým číslem typováno jako number, nebo string?
Pokud může přesáhnout 2^53, musí to být string. JSON.parse během parsování ztrácí přesnost a typování jako number jen zapíše chybu do typu, kde už ji nelze opravit.