यह विषय Stack Overflow पर क्यों छाया हुआ है?
\n\nअगर आपने कभी TypeScript में कोड लिखा है और एक ऐसा ऑब्जेक्ट बनाने की कोशिश की है जहां सभी मान एक ही प्रकार के हों, तो आप शायद इस समस्या से जूझ चुके हैं। Stack Overflow पर सवाल "Enforcing the type of the indexed members of a Typescript object" ने हज़ारों व्यूज़ बटोरे हैं — यह JavaScript से आने वाले डेवलपर्स के लिए एक क्लासिक "गड़बड़" है।
\n\nसोचिए: आपके पास एक कैश डिक्शनरी, डायनामिक कीज़ वाला कॉन्फ़िगरेशन, या सिर्फ़ नंबरों की एक मैप है। आप चाहते हैं कि TypeScript यह सुनिश्चित करे कि ऑब्जेक्ट में हर मान एक स्ट्रिंग, नंबर या कोई जटिल प्रकार हो। लेकिन कंपाइलर गलतियों को अनदेखा क्यों कर देता है? परिचित लग रहा है?
\n\nइस लेख में हम इस समस्या के सभी मुख्य समाधानों पर चर्चा करेंगे: सरल Index Signatures से लेकर उन्नत Mapped Types तक। आप जानेंगे कि कब कौन सा तरीका इस्तेमाल करना है और किन खतरों से बचना है।
समस्या: डायनामिक कीज़ वाला ऑब्जेक्ट
\n\nमान लीजिए, हम एक यूज़र प्रबंधन सिस्टम बना रहे हैं। हमारे पास एक ऑब्जेक्ट है जहां कीज़ यूज़र ID हैं और मान उनके नाम हैं:
\n\ninterface UserCache {\n [userId: string]: string;\n}\n\nconst cache: UserCache = {\n 'user_1': 'Alice',\n 'user_2': 'Bob',\n // 'user_3': 123; // गलती! यह स्ट्रिंग होना चाहिए\n};\n\ncache['user_4'] = 'Charlie'; // ठीक है\ncache['user_5'] = 42; // गलती! Type 'number' 'string' को असाइन नहीं हो सकता\n\n\nलगता है सब काम कर रहा है। लेकिन एक बारीकी है: Index Signature किसी भी स्ट्रिंग की को अनुमति देता है। अगर हम अनुमत कीज़ के सेट को सीमित करना चाहें तो?
तरीका 1: Index Signature — क्लासिक तरीका
\n\nIndex Signature ऑब्जेक्ट के सभी मानों का प्रकार सेट करने का सबसे आसान तरीका है। सिंटैक्स:
\n\ninterface StringMap {\n [key: string]: string;\n}\n\nconst map: StringMap = {\n hello: 'world',\n foo: 'bar',\n};\n\n\nफ़ायदे:
\n- \n
- सरलता और पढ़ने में आसानी \n
- किसी भी संख्या में कीज़ के साथ काम करता है \n
नुकसान:
\n- \n
- कीज़ के सेट को सीमित नहीं कर सकते \n
- सभी मान एक ही प्रकार (या union) के होने चाहिए \n
undefinedमानों के साथ काम नहीं करता (सावधानी ज़रूरी) \n
शुरुआती लोगों की आम गलती: ऐसे ऑब्जेक्ट में एक मेथड जोड़ने की कोशिश करना:
\n\ninterface BadMap {\n [key: string]: string;\n length: number; // गलती! 'number' 'string' को असाइन नहीं हो सकता\n}\n\n\nयह काम नहीं करेगा क्योंकि length सामान्य प्रकार का उल्लंघन करता है। अगर प्रकार मिलाने हैं, तो union या Mapped Types का उपयोग करें।
तरीका 2: Record — बिल्ट-इन हेल्पर
\n\nTypeScript एक बिल्ट-इन प्रकार Record<K, V> प्रदान करता है, जो वही काम करता है लेकिन अधिक संक्षिप्त सिंटैक्स के साथ:
type StringRecord = Record<string, string>;\n\nconst config: StringRecord = {\n apiUrl: 'https://api.example.com',\n timeout: '5000', // ठीक है, स्ट्रिंग\n // port: 8080, // गलती!\n};\n\n// कीज़ को सीमित किया जा सकता है:\ntype AllowedKeys = 'name' | 'email' | 'phone';\ntype UserRecord = Record<AllowedKeys, string>;\n\nconst user: UserRecord = {\n name: 'Alice',\n email: 'alice@example.com',\n phone: '+1234567890',\n // age: '30', // गलती! 'age' AllowedKeys में नहीं है\n};\n\n\nकब उपयोग करें:
\n- \n
- जब जल्दी से एक टाइपिफ़ाइड डिक्शनरी बनानी हो \n
- जब कीज़ एक विशिष्ट सेट (union) हों \n
- ज्ञात फ़ील्ड वाले कॉन्फ़िगरेशन के लिए \n
कमी: Record अलग-अलग कीज़ के लिए अलग-अलग प्रकार सेट नहीं कर सकता। इसके लिए Mapped Types का उपयोग करें।
तरीका 3: Map — ES6 का विकल्प
\n\nअगर आप JavaScript के Map का उपयोग कर रहे हैं, तो TypeScript इसे बखूबी टाइप करता है।