JSON से TypeScript: interface, type guard और जनरेशन की सीमाएँ
मुख्य बात: नमूना JSON से interface उत्पन्न करना आधा काम है: वैकल्पिक फ़ील्ड, null, union, तारीखें और बड़े पूर्णांक के लिए मनुष्य का एक दौर चाहिए, और प्रकार runtime सत्यापन नहीं करते।

सार: टाइपिंग बचाने के लिए interface उत्पन्न करें, फिर वैकल्पिकता और null दोबारा जाँचें, तारीखें और बड़े पूर्णांक स्ट्रिंग रखें, और सीमा पर runtime सत्यापन जोड़ें; जनरेट किए प्रकारों को अनुबंध मानने से आपको कोई सत्यापन नहीं मिलता।
जनरेटर केवल वही एक नमूना देखता है जो आप उसमें चिपकाते हैं। फ़ील्ड अनुपस्थित हो सकते हैं या नहीं, मान null हो सकते हैं या नहीं, और तारीखें किस प्रारूप में हैं — सब अनुमान हैं, और गलत अनुमान आपको ठीक करने हैं।
1. जनरेटर वास्तव में क्या करता है
JSON to TypeScript में नमूना चिपकाएँ और जो interface मिलता है वह आकार का सारांश होता है: यह देखी गई key और मान प्रकार गिनता है, फिर उपलब्ध सबसे संकीर्ण रूप चुनता है।
{
"id": 1,
"email": "ada@example.com",
"profile": { "nickname": "Ada", "age": 36 },
"tags": ["admin", "beta"],
"createdAt": "2026-10-31T08:00:00Z"
}
आउटपुट लगभग ऐसा दिखता है:
interface User {
id: number;
email: string;
profile: { nickname: string; age: number };
tags: string[];
createdAt: string;
}
यह compile होता है, पर यह एक आदर्श मान का वर्णन करता है, endpoint का असली अनुबंध नहीं। जनरेटर नहीं जानता कि email अनुपस्थित हो सकता है या नहीं, tags खाली हो सकते हैं या नहीं, या createdAt में हमेशा समय क्षेत्र होता है या नहीं। यह तो यह भी नहीं जानता कि अन्य रिकॉर्ड्स में id स्ट्रिंग हो सकता है।
इसलिए सही अपेक्षा यह है: जनरेटर टाइपिंग बचाता है, अर्थ मनुष्य देता है।
2. वैकल्पिक फ़ील्ड और null: ? और | null एक नहीं हैं
जनरेट किए आउटपुट का यही हिस्सा सबसे ज़्यादा कॉपी होता है, और सबसे ज़्यादा टूटता भी है।
| TypeScript रूप | अर्थ | मेल खाता JSON |
|---|---|---|
email: string |
हमेशा मौजूद, हमेशा स्ट्रिंग | {"email":"a@b.c"} |
email?: string |
Key अनुपस्थित हो सकती है; मौजूद हो तो null नहीं हो सकती | {} या {"email":"a@b.c"} |
email: string | null |
Key हमेशा मौजूद, मान null हो सकता है | {"email":null} |
email?: string | null |
Key अनुपस्थित हो सकती है और मान null भी हो सकता है | तीनों मामले |
जनरेटर ने एक नमूना देखा है, इसलिए वह आमतौर पर पहला रूप देता है। यदि API वास्तव में null लौटा सकता है, तो प्रकार झूठ बोल रहा है: सब कुछ compile होता है, और फिर email.toLowerCase() runtime पर फेंकता है।
इसे ठीक करने के दो तरीके हैं:
- कई वास्तविक नमूने इकट्ठे करें जो "key अनुपस्थित" और "मान null" दोनों को कवर करें, फिर जनरेटर से दोबारा अनुमान लगवाएँ।
- जनरेट करने के बाद फ़ील्ड को हाथ से
?: T | nullमें बदलें और कोड में दोनों शाखाएँ वास्तव में संभालें।
हर चीज़ को ?: string चिह्नित करने का शॉर्टकट न लें। Optional का अर्थ है "key मौजूद नहीं हो सकती", "मान खाली हो सकता है" नहीं। undefined और null का व्यवहार JSON.stringify में भी अलग है: पहला छोड़ दिया जाता है, दूसरा null के रूप में रखा जाता है।
3. असमान array तत्व और union प्रकार
Array सबसे आसानी से गलत पकड़ी जाने वाली संरचना है। यदि tags तीन रिकॉर्ड्स में ["admin"], [] और ["beta", 3] के रूप में आता है, तो जनरेटर string[], (string | number)[] या unknown[] दे सकता है, जो इस पर निर्भर है कि उसने कौन से नमूने देखे।
मनुष्य को तय करना होगा:
- तत्वों के प्रकार वाकई मिश्रित हैं:
(string | number)[]लिखें और उपयोग के स्थान पर narrow करें। - केवल एक रिकॉर्ड गंदा है: डेटा ठीक करें। उसे
any[]से न छिपाएँ। - खाली array: जनरेटर तत्व प्रकार का अनुमान नहीं लगा सकता और
never[]याany[]पर लौट आता है, इसलिए आपको उसे भरना ही होगा। - स्थिर लंबाई वाले tuple:
[string, string]केवल तब इस्तेमाल करें जब व्यवसाय वाकई लंबाई की गारंटी देता है; वरना बाद में एक तत्व जोड़ना compile त्रुटि बन जाता है।
Object के array में भी key असमान होने पर ध्यान दें:
interface OrderItem {
sku: string;
quantity: number;
discount?: number; // some records do not carry this field
}
वैकल्पिकता तत्व प्रकार पर रखें, array पर नहीं। items?: OrderItem[] का अर्थ है array स्वयं अनुपस्थित हो सकता है, जो पूरी तरह अलग कथन है।
4. तारीखें, बड़े पूर्णांक, enum: वे प्रकार जो JSON में नहीं हैं
JSON में केवल स्ट्रिंग और संख्याएँ हैं; व्यवसाय का अर्थ परंपराओं में रहता है। जनरेटर शाब्दिक रूपों से अनुमान लगाता है, इसलिए उसे हाथ से सुधारें:
| JSON शाब्दिक रूप | सामान्यतः जनरेट किया आउटपुट | क्या होना चाहिए |
|---|---|---|
"2026-10-31T08:00:00Z" |
string |
परिवहन परत में string रखें और उपयोग के स्थान पर बदलें; सीधे Date घोषित न करें |
1717075200000 |
number |
मिलीसेकंड timestamp के लिए number इस्तेमाल करें और इकाई दस्तावेज़ित करें |
"1234567890123456789" |
string |
इसे string ही रखें ताकि number परिशुद्धता न खोए |
1 और "1" मिश्रित |
number | string |
एक रूप में सामान्यीकृत करें; union को व्यवसाय कोड में न धकेलें |
"admin" |
string |
शाब्दिक union 'admin' | 'editor', या साझा enum स्थिरांक |
true और "true" मिश्रित |
boolean | string |
यह प्रकार उत्पन्न करने के बजाय डेटा स्रोत ठीक करें |
सबसे महत्वपूर्ण नियम: JSON में तारीखें हमेशा स्ट्रिंग होती हैं। createdAt: Date लिखना आम आत्म-धोखा है — JSON.parse कभी Date नहीं बनाता, इसलिए runtime मान अब भी स्ट्रिंग है और .getTime() फेंकता है। परिवहन प्रकार string रखें, सीमा पर स्पष्ट रूप से new Date(value) कॉल करें, और Number.isNaN जाँचें।
बड़े पूर्णांक भी वैसे ही चलते हैं। जैसे ही id 2^53 से बढ़ता है, JSON.parse पहले ही परिशुद्धता खो चुका होता है, और number उत्पन्न करना बस bug को प्रकार में पक्का कर देता है। बैकएंड स्ट्रिंग लौटाता है, फ़्रंटएंड उसे स्ट्रिंग रखता है; यही एकमात्र भरोसेमंद विकल्प है।
5. जनरेट किए प्रकार कोई runtime सत्यापन नहीं करते
TypeScript प्रकार compile समय पर मिटा दिए जाते हैं। interface कितना भी सटीक हो, वह {"id": "x"} जैसा रिस्पॉन्स नहीं रोक सकता — वह आपके अपने कोड को सीमित करता है, नेटवर्क से आ रहे बाइट को नहीं।
/**
* Check whether a runtime value is a user object.
* @param value The runtime value to validate
* @returns Whether it satisfies the minimum shape of 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';
}
Type guard केवल उन फ़ील्ड को कवर करता है जो आप जाँच में लिखते हैं। फ़ील्ड बढ़ने के साथ हाथ से लिखे guard रखरखाव की दूसरी परिभाषा बन जाते हैं, जो interface से दूर खिसकती जाती है।
व्यावहारिक समझौता ऐसा दिखता है:
| तरीका | Runtime सत्यापन | प्रकारों का स्रोत | किसके लिए उपयुक्त |
|---|---|---|---|
| केवल interface | नहीं | हाथ से लिखा या जनरेट किया | भरोसेमंद आंतरिक डेटा, compile-समय बाधाएँ |
| Interface और हाथ से लिखा guard | हाँ | दो परिभाषाएँ जो खिसकती हैं | कम फ़ील्ड, स्पष्ट सीमा |
| zod / valibot | हाँ | schema से अनुमानित प्रकार | बाहरी API, फ़ॉर्म, config |
| JSON Schema और validator | हाँ | schema से जनरेट किए प्रकार | क्रॉस-भाषा अनुबंध, दस्तावेज़ीकरण |
यदि डेटा बाहर से आता है, तो schema को सत्य का एकमात्र स्रोत बनाएँ और z.infer या json-schema-to-ts से प्रकार निकालें। तब "सत्यापन पार हुआ" और "प्रकार सही है" एक ही तथ्य बन जाते हैं, और खिसकने के लिए दूसरी परिभाषा नहीं रहती। पूरे दो-तरफ़ा समन्वय तरीके के लिए देखें Schema और TypeScript प्रकार समन्वय।
6. Interface या type: जनरेट किए आउटपुट पर बहस न करें
जनरेटर आमतौर पर interface देते हैं, पर ज़्यादातर स्थितियों में दोनों विनिमेय हैं। वास्तव में तीन क्षमताएँ मायने रखती हैं:
- Declaration merging:
interfaceको कई बार घोषित किया जा सकता है और वह स्वतः मिल जाता है;typeनहीं। यह तीसरे पक्ष के प्रकारों को पैच करते समय मदद करता है, पर व्यवसाय मॉडल इससे बचें — merging "अतिरिक्त फ़ील्ड" को compiler की नज़र बचाकर आने देता है। - Union और mapped प्रकार:
typeसीधेA | B,Partial<T>और template literal प्रकार व्यक्त कर सकता है;interfaceनहीं। - Recursion: दोनों समर्थन करते हैं, पर जटिल recursive आकार
typeके रूप में अधिक स्वाभाविक पढ़े जाते हैं।
अधिक उपयोगी टीम नियम: object आकार के लिए interface, union, utility प्रकार और function प्रकार के लिए type इस्तेमाल करें। जनरेट किए आउटपुट को उसके डिफ़ॉल्ट पर छोड़ दें और समीक्षा का समय वैकल्पिकता तथा runtime सत्यापन पर लगाएँ।
7. टूलचेन: जनरेट करने के तीन तरीके
| तरीका | इनपुट | ताकत | सीमा |
|---|---|---|---|
| साइट का JSON to TypeScript | नमूना JSON | तुरंत काम करता है, स्थानीय रूप से संसाधित | केवल वर्तमान नमूने पर आधारित; हाथ से narrow करना पड़ता है |
| quicktype | JSON / JSON Schema | बहु-भाषा आउटपुट, CLI एकीकरण | रूढ़िवादी अनुमान, union बहुत चौड़े हो सकते हैं |
| json-schema-to-typescript | JSON Schema | अनुबंध-चालित और दोहराने योग्य | पहले भरोसेमंद schema चाहिए |
quicktype का एक सामान्य आह्वान:
npx quicktype -s json -o user.ts user.json --just-types
--just-types केवल प्रकार देता है और कोई runtime रूपांतरण कोड नहीं, जो उन परियोजनाओं के लिए उपयुक्त है जिनके पास पहले से पार्सिंग परत है। यदि schema हाथ से लिखा गया है या बैकएंड से आता है, तो json-schema-to-typescript बेहतर बैठता है क्योंकि वह required, enum और additionalProperties का सम्मान करता है — ऐसी बाधाएँ जिन्हें नमूना अनुमान देख ही नहीं सकता।
Schema जनरेशन के अपने जाल हैं, जैसे हर फ़ील्ड को required चिह्नित करना या null को चुपचाप optional बना देना; देखें JSON Schema जनरेटर की सामान्य गड़बड़ियाँ।
8. जनरेट किए प्रकार commit करने से पहले चेकलिस्ट
आउटपुट commit करने से पहले इन पाँच बिंदुओं से गुज़रें:
- फ़ील्ड नाम सामान्यीकृत करें:
userIdऔरuser_idको साथ न रहने दें; रूपांतरण परत में एक बार मैप करें। - वैकल्पिकता दोबारा जाँचें: वास्तविक रिस्पॉन्स से तुलना करें और खोजें कि कौन से फ़ील्ड अनुपस्थित या
nullहो सकते हैं। - तारीखें और id स्ट्रिंग रखें: प्रकार में इकाई और प्रारूप दस्तावेज़ित करें, और उपयोग के स्थान पर स्पष्ट रूप से पार्स करें।
anyऔरunknownकम करें: जहाँ जनरेटर नेanyदिया, वहाँ या तो प्रकार जोड़ें याunknownरखें और guard जोड़ें।- Runtime सत्यापन जोड़ें: बाहरी डेटा कम से कम एक बार सत्यापित करें; interface से गंदा डेटा पकड़ने की अपेक्षा न रखें।
यदि परियोजना कई JSON लाइब्रेरी इस्तेमाल करती है (JSON.parse, superjson, lossless-json), तो बड़े पूर्णांक और तारीखों के लिए उनका व्यवहार अलग है, इसलिए चयन के समय इसकी पुष्टि करें। देखें JSON पार्सिंग लाइब्रेरी का चयन।
जनरेटर हर वैकल्पिक फ़ील्ड को required क्यों चिह्नित करता है?
उसने एक ही नमूना देखा, इसलिए जो भी key दिखी वह हमेशा मौजूद मान ली गई। अलग-अलग मामले कवर करने वाले कई नमूने दें, या JSON Schema से जनरेट करें — वहाँ का required array प्रामाणिक स्रोत है।
interface में Date घोषित करना runtime पर क्यों विफल होता है?
JSON.parse केवल स्ट्रिंग, संख्याएँ, boolean, null, object और array उत्पन्न करता है; वह कभी Date नहीं बनाता। Date घोषित करना compile-समय का वादा है जबकि runtime मान स्ट्रिंग ही रहता है। परिवहन परत में string रखें और सीमा पर बदलें।
क्या जनरेट किए प्रकार सत्यापन के रूप में इस्तेमाल हो सकते हैं?
नहीं। TypeScript प्रकार compile के बाद मिट जाते हैं और runtime डेटा पर कुछ नहीं थोपते। बाहरी डेटा zod, valibot, ajv या हाथ से लिखे type guard से सत्यापित करें।
बड़े पूर्णांक id को number या string टाइप करूँ?
यदि वह 2^53 से बड़ा हो सकता है, तो उसे string ही होना चाहिए। JSON.parse पार्सिंग के दौरान परिशुद्धता खो देता है, और उसे number टाइप करना बस bug को प्रकार में लिख देता है जहाँ उसे फिर सुधारा नहीं जा सकता।