const assertions और union types जैसे शक्तिशाली TypeScript enum विकल्पों को जानें। वैश्विक विकास के संदर्भ में स्वच्छ, अधिक रखरखाव योग्य कोड के लिए उनके लाभ, कमियों और व्यावहारिक अनुप्रयोगों को समझें।
TypeScript Enum के विकल्प: मजबूत कोड के लिए Const Assertions और Union Types को समझना
TypeScript, जावास्क्रिप्ट का एक शक्तिशाली सुपरसेट, वेब डेवलपमेंट की गतिशील दुनिया में स्टैटिक टाइपिंग लाता है। इसकी कई विशेषताओं में, enum कीवर्ड लंबे समय से नामित स्थिरांकों के एक सेट को परिभाषित करने के लिए पसंदीदा रहा है। Enums संबंधित मानों के एक निश्चित संग्रह का प्रतिनिधित्व करने का एक स्पष्ट तरीका प्रदान करते हैं, जिससे पठनीयता और टाइप सुरक्षा बढ़ती है।
हालांकि, जैसे-जैसे TypeScript इकोसिस्टम परिपक्व होता है और परियोजनाओं की जटिलता और पैमाने में वृद्धि होती है, दुनिया भर में डेवलपर्स पारंपरिक रूप से enums की उपयोगिता पर सवाल उठा रहे हैं। जबकि सरल मामलों के लिए यह सीधा है, enums कुछ व्यवहार और रनटाइम विशेषताओं को पेश करते हैं जो कभी-कभी अप्रत्याशित मुद्दों का कारण बन सकते हैं, बंडल आकार को प्रभावित कर सकते हैं, या ट्री-शेकिंग अनुकूलन को जटिल बना सकते हैं। इसके कारण विकल्पों की व्यापक खोज हुई है।
यह व्यापक मार्गदर्शिका TypeScript enums के दो प्रमुख और अत्यधिक प्रभावी विकल्पों पर गहराई से चर्चा करती है: Union Types with String/Numeric Literals और Const Assertions (as const)। हम उनके तंत्र, व्यावहारिक अनुप्रयोगों, लाभों और ट्रेड-ऑफ का पता लगाएंगे, जिससे आपको अपने प्रोजेक्ट्स के लिए सूचित डिजाइन निर्णय लेने का ज्ञान मिलेगा, चाहे उनका आकार कुछ भी हो या उन पर काम करने वाली वैश्विक टीम हो। हमारा लक्ष्य आपको अधिक मजबूत, रखरखाव योग्य और कुशल TypeScript कोड लिखने के लिए सशक्त बनाना है।
TypeScript Enum: एक त्वरित पुनर्कथन
विकल्पों में गोता लगाने से पहले, आइए संक्षेप में पारंपरिक TypeScript enum पर फिर से विचार करें। Enums डेवलपर्स को नामित स्थिरांकों का एक सेट परिभाषित करने की अनुमति देते हैं, जिससे कोड अधिक पठनीय हो जाता है और "मैजिक स्ट्रिंग्स" या "मैजिक नंबर्स" को एप्लिकेशन में बिखरने से रोका जा सकता है। वे दो प्राथमिक रूपों में आते हैं: न्यूमेरिक और स्ट्रिंग एनम।
न्यूमेरिक एनम (Numeric Enums)
डिफ़ॉल्ट रूप से, TypeScript enums न्यूमेरिक होते हैं। पहले सदस्य को 0 से शुरू किया जाता है, और प्रत्येक बाद के सदस्य को स्वतः बढ़ाया जाता है।
enum Direction {
Up,
Down,
Left,
Right,
}
let currentDirection: Direction = Direction.Up;
console.log(currentDirection); // Outputs: 0
console.log(Direction.Left); // Outputs: 2
आप न्यूमेरिक एनम सदस्यों को मैन्युअल रूप से भी आरंभ कर सकते हैं:
enum StatusCode {
Success = 200,
NotFound = 404,
ServerError = 500,
}
let status: StatusCode = StatusCode.NotFound;
console.log(status); // Outputs: 404
न्यूमेरिक एनम की एक अजीब विशेषता रिवर्स मैपिंग है। रनटाइम पर, एक न्यूमेरिक एनम एक जावास्क्रिप्ट ऑब्जेक्ट में संकलित होता है जो नामों को मानों और मानों को वापस नामों पर मैप करता है।
enum UserRole {
Admin = 1,
Editor,
Viewer,
}
console.log(UserRole[1]); // Outputs: "Admin"
console.log(UserRole.Editor); // Outputs: 2
console.log(UserRole[2]); // Outputs: "Editor"
/*
Compiles to JavaScript:
var UserRole;
(function (UserRole) {
UserRole[UserRole["Admin"] = 1] = "Admin";
UserRole[UserRole["Editor"] = 2] = "Editor";
UserRole[UserRole["Viewer"] = 3] = "Viewer";
})(UserRole || (UserRole = {}));
*/
स्ट्रिंग एनम (String Enums)
स्ट्रिंग एनम को अक्सर रनटाइम पर उनकी पठनीयता के लिए पसंद किया जाता है, क्योंकि वे स्वतः-बढ़ने वाली संख्याओं पर निर्भर नहीं करते हैं। प्रत्येक सदस्य को एक स्ट्रिंग लिटरल के साथ आरंभ किया जाना चाहिए।
enum UserPermission {
Read = "READ_PERMISSION",
Write = "WRITE_PERMISSION",
Delete = "DELETE_PERMISSION",
}
let permission: UserPermission = UserPermission.Write;
console.log(permission); // Outputs: "WRITE_PERMISSION"
स्ट्रिंग एनम को नहीं मिलती है रिवर्स मैपिंग, जो आम तौर पर अप्रत्याशित रनटाइम व्यवहार से बचने और उत्पन्न जावास्क्रिप्ट आउटपुट को कम करने के लिए एक अच्छी बात है।
Enums के मुख्य विचार और संभावित नुकसान
हालांकि एनम सुविधा प्रदान करते हैं, वे कुछ विशेषताओं के साथ आते हैं जिन पर सावधानीपूर्वक विचार करने की आवश्यकता है:
- रनटाइम ऑब्जेक्ट्स: न्यूमेरिक और स्ट्रिंग दोनों एनम रनटाइम पर जावास्क्रिप्ट ऑब्जेक्ट उत्पन्न करते हैं। इसका मतलब है कि वे आपके एप्लिकेशन के बंडल आकार में योगदान करते हैं, भले ही आप उन्हें केवल टाइप-चेकिंग के लिए उपयोग करते हों। छोटे प्रोजेक्ट्स के लिए, यह नगण्य हो सकता है, लेकिन कई एनम वाले बड़े पैमाने के एप्लिकेशन में, यह जुड़ सकता है।
- ट्री-शेकिंग का अभाव: क्योंकि एनम रनटाइम ऑब्जेक्ट्स हैं, उन्हें अक्सर वेबपैक या रोलअप जैसे आधुनिक बंडलर्स द्वारा प्रभावी ढंग से ट्री-शेक नहीं किया जाता है। यदि आप एक एनम को परिभाषित करते हैं लेकिन उसके केवल एक या दो सदस्यों का उपयोग करते हैं, तो भी पूरा एनम ऑब्जेक्ट आपके अंतिम बंडल में शामिल हो सकता है। इससे आवश्यकता से अधिक बड़ी फ़ाइलें बन सकती हैं।
- रिवर्स मैपिंग (न्यूमेरिक एनम): न्यूमेरिक एनम की रिवर्स मैपिंग सुविधा, हालांकि कभी-कभी उपयोगी होती है, भ्रम और अप्रत्याशित व्यवहार का स्रोत भी हो सकती है। यह जावास्क्रिप्ट आउटपुट में अतिरिक्त कोड जोड़ता है और हमेशा वांछित कार्यक्षमता नहीं हो सकती है। उदाहरण के लिए, न्यूमेरिक एनम को क्रमबद्ध करने से कभी-कभी केवल संख्या संग्रहीत हो सकती है, जो स्ट्रिंग जितनी वर्णनात्मक नहीं हो सकती है।
- ट्रांसपिलेशन ओवरहेड: एनम को जावास्क्रिप्ट ऑब्जेक्ट में संकलित करना केवल स्थिरांक चर को परिभाषित करने की तुलना में बिल्ड प्रक्रिया में थोड़ा ओवरहेड जोड़ता है।
- सीमित पुनरावृत्ति: एनम मानों पर सीधे पुनरावृत्ति करना गैर-तुच्छ हो सकता है, विशेष रूप से न्यूमेरिक एनम के साथ रिवर्स मैपिंग के कारण। आपको अक्सर केवल वांछित मान प्राप्त करने के लिए सहायक कार्यों या विशिष्ट लूप की आवश्यकता होती है।
ये बिंदु इस बात पर प्रकाश डालते हैं कि क्यों कई वैश्विक विकास टीमें, विशेष रूप से प्रदर्शन और बंडल आकार पर ध्यान केंद्रित करने वाली टीमें, ऐसे विकल्पों की ओर देख रही हैं जो रनटाइम फुटप्रिंट या अन्य जटिलताओं के बिना समान प्रकार की सुरक्षा प्रदान करते हैं।
विकल्प 1: लिटरल्स के साथ यूनियन टाइप्स (Union Types with Literals)
TypeScript में एनम के सबसे सीधे और शक्तिशाली विकल्पों में से एक यूनियन टाइप्स का उपयोग स्ट्रिंग या न्यूमेरिक लिटरल्स के साथ है। यह दृष्टिकोण TypeScript के मजबूत प्रकार प्रणाली का लाभ उठाता है ताकि कंपाइल-टाइम पर विशिष्ट, अनुमत मानों का एक सेट परिभाषित किया जा सके, बिना रनटाइम पर कोई नया निर्माण किए।
यूनियन टाइप्स क्या हैं?
एक यूनियन टाइप एक मान का वर्णन करता है जो कई प्रकारों में से एक हो सकता है। उदाहरण के लिए, string | number का मतलब है कि एक चर या तो एक स्ट्रिंग या एक संख्या रख सकता है। जब लिटरल प्रकारों (जैसे, "success", 404) के साथ जोड़ा जाता है, तो आप एक ऐसा प्रकार परिभाषित कर सकते हैं जो केवल पूर्वनिर्धारित मानों का एक विशिष्ट सेट रख सकता है।
व्यावहारिक उदाहरण: यूनियन टाइप्स के साथ स्टेटस को परिभाषित करना
आइए एक सामान्य परिदृश्य पर विचार करें: डेटा प्रोसेसिंग जॉब या उपयोगकर्ता के खाते के लिए संभावित स्टेटस का एक सेट परिभाषित करना। यूनियन टाइप्स के साथ, यह साफ और संक्षिप्त दिखता है:
type JobStatus = "PENDING" | "IN_PROGRESS" | "COMPLETED" | "FAILED";
function processJob(status: JobStatus): void {
if (status === "COMPLETED") {
console.log("Job finished successfully.");
} else if (status === "FAILED") {
console.log("Job encountered an error.");
} else {
console.log(`Job is currently ${status}.`);
}
}
let currentJobStatus: JobStatus = "IN_PROGRESS";
processJob(currentJobStatus);
// This would result in a compile-time error:
// let invalidStatus: JobStatus = "CANCELLED"; // Error: Type '"CANCELLED"' is not assignable to type 'JobStatus'.
संख्यात्मक मानों के लिए, पैटर्न समान है:
type HttpCode = 200 | 400 | 404 | 500;
function handleResponse(code: HttpCode): void {
if (code === 200) {
console.log("Operation successful.");
} else if (code === 404) {
console.log("Resource not found.");
}
}
let responseStatus: HttpCode = 200;
handleResponse(responseStatus);
ध्यान दें कि हम यहां एक type उपनाम को कैसे परिभाषित कर रहे हैं। यह पूरी तरह से एक कंपाइल-टाइम निर्माण है। जब जावास्क्रिप्ट में संकलित किया जाता है, तो JobStatus बस गायब हो जाता है, और लिटरल स्ट्रिंग्स/नंबर्स का सीधे उपयोग किया जाता है।
लिटरल्स के साथ यूनियन टाइप्स के लाभ
यह दृष्टिकोण कई आकर्षक लाभ प्रदान करता है:
- पूरी तरह से कंपाइल-टाइम: संकलन के दौरान यूनियन टाइप्स पूरी तरह से मिटा दिए जाते हैं। वे रनटाइम पर कोई जावास्क्रिप्ट कोड उत्पन्न नहीं करते हैं, जिससे छोटे बंडल आकार और तेज़ एप्लिकेशन स्टार्टअप समय होता है। यह प्रदर्शन-महत्वपूर्ण अनुप्रयोगों और उन लोगों के लिए एक महत्वपूर्ण लाभ है जो वैश्विक रूप से तैनात हैं जहां हर किलोबाइट मायने रखता है।
- उत्कृष्ट प्रकार सुरक्षा: TypeScript परिभाषित लिटरल प्रकारों के विरुद्ध असाइनमेंट की कठोरता से जाँच करता है, जिससे यह मजबूत गारंटी मिलती है कि केवल मान्य मानों का उपयोग किया जाता है। यह टाइपो या गलत मानों से जुड़े सामान्य बग को रोकता है।
- इष्टतम ट्री-शेकिंग: चूंकि कोई रनटाइम ऑब्जेक्ट नहीं है, यूनियन टाइप्स स्वाभाविक रूप से ट्री-शेकिंग का समर्थन करते हैं। आपका बंडलर केवल आपके द्वारा उपयोग किए जाने वाले वास्तविक स्ट्रिंग या न्यूमेरिक लिटरल्स को शामिल करता है, न कि एक संपूर्ण ऑब्जेक्ट।
- पठनीयता: सरल, विशिष्ट मानों के एक निश्चित सेट के लिए, प्रकार की परिभाषा अक्सर बहुत स्पष्ट और समझने में आसान होती है।
- सरलता: कोई नया भाषा निर्माण या जटिल संकलन कलाकृतियाँ पेश नहीं की जाती हैं। यह सिर्फ मौलिक TypeScript प्रकार की विशेषताओं का लाभ उठा रहा है।
- प्रत्यक्ष मान पहुंच: आप सीधे स्ट्रिंग या संख्या मानों के साथ काम करते हैं, जो क्रमबद्धता और डीसेरियलाइजेशन को सरल बनाता है, खासकर जब एपीआई या डेटाबेस के साथ इंटरैक्ट करते हैं जो विशिष्ट स्ट्रिंग पहचानकर्ताओं की अपेक्षा करते हैं।
लिटरल्स के साथ यूनियन टाइप्स की कमियां
शक्तिशाली होते हुए भी, यूनियन टाइप्स की कुछ सीमाएँ भी हैं:
- संबद्ध डेटा के लिए दोहराव: यदि आपको प्रत्येक "एनम" सदस्य के साथ अतिरिक्त डेटा या मेटाडेटा को संबद्ध करने की आवश्यकता है (जैसे, एक प्रदर्शन लेबल, एक आइकन, एक रंग), तो आप इसे सीधे यूनियन टाइप परिभाषा के भीतर नहीं कर सकते। आपको आमतौर पर एक अलग मैपिंग ऑब्जेक्ट की आवश्यकता होगी।
- सभी मानों की कोई सीधी पुनरावृत्ति नहीं: रनटाइम पर एक यूनियन टाइप से सभी संभावित मानों की एक सरणी प्राप्त करने का कोई अंतर्निहित तरीका नहीं है। उदाहरण के लिए, आप सीधे
JobStatusसे["PENDING", "IN_PROGRESS", "COMPLETED", "FAILED"]आसानी से प्राप्त नहीं कर सकते हैं। इसके लिए अक्सर मानों की एक अलग सरणी बनाए रखने की आवश्यकता होती है यदि आपको उन्हें यूआई में प्रदर्शित करने की आवश्यकता है (जैसे, एक ड्रॉपडाउन मेनू)। - कम केंद्रीकृत: यदि मानों के सेट की आवश्यकता एक प्रकार के रूप में और रनटाइम मानों की एक सरणी के रूप में दोनों है, तो आप खुद को सूची को दो बार परिभाषित करते हुए पा सकते हैं (एक बार एक प्रकार के रूप में, एक बार रनटाइम सरणी के रूप में), जो डीसिंक्रनाइज़ेशन की क्षमता पेश कर सकता है।
इन कमियों के बावजूद, कई परिदृश्यों के लिए, यूनियन टाइप्स एक स्वच्छ, प्रदर्शनकारी और प्रकार-सुरक्षित समाधान प्रदान करते हैं जो आधुनिक जावास्क्रिप्ट विकास प्रथाओं के साथ अच्छी तरह से मेल खाता है।
विकल्प 2: कॉन्स्ट असर्शन (as const)
as const असर्शन, जिसे TypeScript 3.4 में पेश किया गया था, एक और अविश्वसनीय रूप से शक्तिशाली उपकरण है जो एनम का एक उत्कृष्ट विकल्प प्रदान करता है, खासकर जब आपको एक रनटाइम ऑब्जेक्ट और मजबूत प्रकार अनुमान की आवश्यकता होती है। यह TypeScript को लिटरल एक्सप्रेशन के लिए सबसे संकीर्ण संभव प्रकार का अनुमान लगाने की अनुमति देता है।
कॉन्स्ट असर्शन क्या हैं?
जब आप एक चर, एक सरणी, या एक ऑब्जेक्ट लिटरल पर as const लागू करते हैं, तो TypeScript उस लिटरल के भीतर सभी गुणों को readonly के रूप में मानता है और व्यापक प्रकारों के बजाय उनके लिटरल प्रकारों का अनुमान लगाता है (जैसे, string के बजाय "foo", number के बजाय 123)। यह रनटाइम डेटा संरचनाओं से अत्यधिक विशिष्ट यूनियन प्रकारों को प्राप्त करना संभव बनाता है।
व्यावहारिक उदाहरण: as const के साथ एक "स्यूडो-एनम" ऑब्जेक्ट बनाना
आइए हमारे जॉब स्टेटस उदाहरण पर फिर से विचार करें। as const के साथ, हम अपने स्टेटस के लिए सत्य का एक एकल स्रोत परिभाषित कर सकते हैं, जो रनटाइम ऑब्जेक्ट और प्रकार परिभाषाओं के आधार दोनों के रूप में कार्य करता है।
const JobStatuses = {
PENDING: "PENDING",
IN_PROGRESS: "IN_PROGRESS",
COMPLETED: "COMPLETED",
FAILED: "FAILED",
} as const;
// JobStatuses.PENDING is now inferred as type "PENDING" (not just string)
// JobStatuses is inferred as type {
// readonly PENDING: "PENDING";
// readonly IN_PROGRESS: "IN_PROGRESS";
// readonly COMPLETED: "COMPLETED";
// readonly FAILED: "FAILED";
// }
इस बिंदु पर, JobStatuses रनटाइम पर एक जावास्क्रिप्ट ऑब्जेक्ट है, ठीक एक नियमित एनम की तरह। हालांकि, इसका प्रकार अनुमान कहीं अधिक सटीक है।
यूनियन टाइप्स के लिए typeof और keyof के साथ संयोजन
वास्तविक शक्ति तब उभरती है जब हम as const को TypeScript के typeof और keyof ऑपरेटरों के साथ जोड़ते हैं ताकि ऑब्जेक्ट के मानों या कुंजियों से एक यूनियन प्रकार प्राप्त किया जा सके।
const JobStatuses = {
PENDING: "PENDING",
IN_PROGRESS: "IN_PROGRESS",
COMPLETED: "COMPLETED",
FAILED: "FAILED",
} as const;
// Type representing the keys (e.g., "PENDING" | "IN_PROGRESS" | ...)
type JobStatusKeys = keyof typeof JobStatuses;
// Type representing the values (e.g., "PENDING" | "IN_PROGRESS" | ...)
type JobStatusValues = typeof JobStatuses[keyof typeof JobStatuses];
function processJobWithConstAssertion(status: JobStatusValues): void {
if (status === JobStatuses.COMPLETED) {
console.log("Job finished successfully.");
} else if (status === JobStatuses.FAILED) {
console.log("Job encountered an error.");
} else {
console.log(`Job is currently ${status}.`);
}
}
let currentJobStatusFromObject: JobStatusValues = JobStatuses.IN_PROGRESS;
processJobWithConstAssertion(currentJobStatusFromObject);
// This would result in a compile-time error:
// let invalidStatusFromObject: JobStatusValues = "CANCELLED"; // Error!
यह पैटर्न दोनों दुनियाओं का सर्वश्रेष्ठ प्रदान करता है: पुनरावृत्ति या सीधे संपत्ति पहुंच के लिए एक रनटाइम ऑब्जेक्ट, और सख्त प्रकार की जाँच के लिए एक कंपाइल-टाइम यूनियन प्रकार।
व्युत्पन्न यूनियन टाइप्स के साथ कॉन्स्ट असर्शन के लाभ
- सत्य का एकल स्रोत: आप अपने स्थिरांक को एक सादे जावास्क्रिप्ट ऑब्जेक्ट में एक बार परिभाषित करते हैं, और उससे रनटाइम एक्सेस और कंपाइल-टाइम प्रकार दोनों प्राप्त करते हैं। यह विभिन्न विकास टीमों में दोहराव को काफी कम करता है और रखरखाव में सुधार करता है।
- प्रकार सुरक्षा: शुद्ध यूनियन प्रकारों के समान, आपको उत्कृष्ट प्रकार सुरक्षा मिलती है, यह सुनिश्चित करते हुए कि केवल पूर्वनिर्धारित मानों का उपयोग किया जाता है।
- रनटाइम पर पुनरावृत्ति: चूंकि
JobStatusesएक सादा जावास्क्रिप्ट ऑब्जेक्ट है, आपObject.keys(),Object.values(), याObject.entries()जैसे मानक जावास्क्रिप्ट तरीकों का उपयोग करके आसानी से इसकी कुंजियों या मानों पर पुनरावृत्ति कर सकते हैं। यह गतिशील यूआई (जैसे, ड्रॉपडाउन को पॉप्युलेट करना) या लॉगिंग के लिए अमूल्य है। - संबद्ध डेटा: यह पैटर्न स्वाभाविक रूप से प्रत्येक "एनम" सदस्य के साथ अतिरिक्त डेटा को संबद्ध करने का समर्थन करता है।
- बेहतर ट्री-शेकिंग क्षमता (एनम की तुलना में): जबकि
as constएक रनटाइम ऑब्जेक्ट बनाता है, यह एक मानक जावास्क्रिप्ट ऑब्जेक्ट है। आधुनिक बंडलर आम तौर पर अप्रयुक्त गुणों या यहां तक कि पूरे ऑब्जेक्ट को ट्री-शेक करने में अधिक प्रभावी होते हैं यदि वे संदर्भित नहीं होते हैं, TypeScript के एनम संकलन आउटपुट की तुलना में। हालांकि, यदि ऑब्जेक्ट बड़ा है और केवल कुछ गुणों का उपयोग किया जाता है, तो भी पूरा ऑब्जेक्ट शामिल हो सकता है यदि इसे इस तरह से आयात किया जाता है जो दानेदार ट्री-शेकिंग को रोकता है। - लचीलापन: आप ऐसे मानों को परिभाषित कर सकते हैं जो केवल स्ट्रिंग या संख्याएं ही नहीं बल्कि आवश्यकता पड़ने पर अधिक जटिल ऑब्जेक्ट भी हों, जिससे यह एक अत्यधिक लचीला पैटर्न बन जाता है।
const FileOperations = {
UPLOAD: {
label: "Upload File",
icon: "upload-icon.svg",
permission: "can_upload"
},
DOWNLOAD: {
label: "Download File",
icon: "download-icon.svg",
permission: "can_download"
},
DELETE: {
label: "Delete File",
icon: "delete-icon.svg",
permission: "can_delete"
},
} as const;
type FileOperationType = keyof typeof FileOperations; // "UPLOAD" | "DOWNLOAD" | "DELETE"
type FileOperationDetail = typeof FileOperations[keyof typeof FileOperations]; // { label: string; icon: string; permission: string; }
function performOperation(opType: FileOperationType) {
const details = FileOperations[opType];
console.log(`Performing: ${details.label} (Permission: ${details.permission})`);
}
performOperation("UPLOAD");
कॉन्स्ट असर्शन की कमियां
- रनटाइम ऑब्जेक्ट की उपस्थिति: शुद्ध यूनियन प्रकारों के विपरीत, यह दृष्टिकोण अभी भी रनटाइम पर एक जावास्क्रिप्ट ऑब्जेक्ट बनाता है। यद्यपि यह एक मानक ऑब्जेक्ट है और अक्सर एनम की तुलना में ट्री-शेकिंग के लिए बेहतर है, यह पूरी तरह से मिटाया नहीं जाता है।
- थोड़ा अधिक वर्बोज़ प्रकार परिभाषा: यूनियन प्रकार को प्राप्त करने (
keyof typeof ...याtypeof ...[keyof typeof ...]) के लिए एक यूनियन प्रकार के लिए केवल लिटरल्स को सूचीबद्ध करने की तुलना में थोड़ा अधिक सिंटैक्स की आवश्यकता होती है। - दुरुपयोग की संभावना: यदि सावधानी से उपयोग नहीं किया जाता है, तो एक बहुत बड़ा
as constऑब्जेक्ट अभी भी बंडल आकार में महत्वपूर्ण योगदान दे सकता है यदि इसकी सामग्री को मॉड्यूल सीमाओं के पार प्रभावी ढंग से ट्री-शेक नहीं किया जाता है।
उन परिदृश्यों के लिए जहां आपको मजबूत कंपाइल-टाइम प्रकार जाँच और मानों का एक रनटाइम संग्रह दोनों की आवश्यकता होती है, जिन पर पुनरावृत्ति की जा सकती है या संबद्ध डेटा प्रदान किया जा सकता है, as const अक्सर दुनिया भर में TypeScript डेवलपर्स के बीच पसंदीदा विकल्प होता है।
विकल्पों की तुलना: कब क्या उपयोग करें?
यूनियन प्रकारों और कॉन्स्ट असर्शन के बीच चयन काफी हद तक आपकी विशिष्ट आवश्यकताओं पर निर्भर करता है, जो रनटाइम उपस्थिति, पुनरावृत्ति और क्या आपको अपने स्थिरांक के साथ अतिरिक्त डेटा को संबद्ध करने की आवश्यकता है, से संबंधित है। आइए निर्णय लेने वाले कारकों को तोड़ते हैं।
सरलता बनाम मजबूती
- यूनियन टाइप्स: जब आपको कंपाइल-टाइम पर केवल विशिष्ट स्ट्रिंग या न्यूमेरिक मानों का एक प्रकार-सुरक्षित सेट चाहिए होता है, तो वे अंतिम सरलता प्रदान करते हैं। वे सबसे हल्के विकल्प हैं।
- कॉन्स्ट असर्शन: जब आपको कंपाइल-टाइम प्रकार सुरक्षा और एक रनटाइम ऑब्जेक्ट दोनों की आवश्यकता होती है, जिसे क्वेरी किया जा सकता है, पुनरावृत्त किया जा सकता है, या अतिरिक्त मेटाडेटा के साथ बढ़ाया जा सकता है, तो एक अधिक मजबूत पैटर्न प्रदान करते हैं। प्रारंभिक सेटअप थोड़ा अधिक वर्बोज़ है, लेकिन यह सुविधाओं में भुगतान करता है।
रनटाइम बनाम कंपाइल-टाइम उपस्थिति
- यूनियन टाइप्स: पूरी तरह से कंपाइल-टाइम निर्माण हैं। वे बिल्कुल कोई जावास्क्रिप्ट कोड उत्पन्न नहीं करते हैं। यह उन अनुप्रयोगों के लिए आदर्श है जहां बंडल आकार को कम करना सर्वोपरि है, और मान स्वयं रनटाइम पर ऑब्जेक्ट के रूप में एक्सेस किए बिना पर्याप्त हैं।
- कॉन्स्ट असर्शन: रनटाइम पर एक सादा जावास्क्रिप्ट ऑब्जेक्ट उत्पन्न करते हैं। यह ऑब्जेक्ट आपके जावास्क्रिप्ट कोड में सुलभ और प्रयोग करने योग्य है। यद्यपि यह बंडल आकार में जोड़ता है, यह आम तौर पर TypeScript एनम की तुलना में अधिक कुशल है और ट्री-शेकिंग के लिए बेहतर उम्मीदवार है।
पुनरावृत्ति आवश्यकताएँ
- यूनियन टाइप्स: रनटाइम पर सभी संभावित मानों पर पुनरावृत्ति करने का कोई सीधा तरीका प्रदान नहीं करते हैं। यदि आपको एक ड्रॉपडाउन मेनू को पॉप्युलेट करने या सभी विकल्पों को प्रदर्शित करने की आवश्यकता है, तो आपको इन मानों की एक अलग सरणी को परिभाषित करने की आवश्यकता होगी, जिससे संभावित रूप से दोहराव हो सकता है।
- कॉन्स्ट असर्शन: यहां उत्कृष्टता प्राप्त करते हैं। चूंकि आप एक मानक जावास्क्रिप्ट ऑब्जेक्ट के साथ काम कर रहे हैं, आप आसानी से
Object.keys(),Object.values(), याObject.entries()का उपयोग करके क्रमशः कुंजियों, मानों, या कुंजी-मान युग्मों की एक सरणी प्राप्त कर सकते हैं। यह उन्हें गतिशील यूआई या रनटाइम गणना की आवश्यकता वाले किसी भी परिदृश्य के लिए एकदम सही बनाता है।
const PaymentMethods = {
CREDIT_CARD: "Credit Card",
PAYPAL: "PayPal",
BANK_TRANSFER: "Bank Transfer",
} as const;
type PaymentMethodType = keyof typeof PaymentMethods;
// Get all keys (e.g., for internal logic)
const methodKeys = Object.keys(PaymentMethods) as PaymentMethodType[];
console.log(methodKeys); // ["CREDIT_CARD", "PAYPAL", "BANK_TRANSFER"]
// Get all values (e.g., for display in a dropdown)
const methodLabels = Object.values(PaymentMethods);
console.log(methodLabels); // ["Credit Card", "PayPal", "Bank Transfer"]
// Get key-value pairs (e.g., for mapping)
const methodEntries = Object.entries(PaymentMethods);
console.log(methodEntries); // [["CREDIT_CARD", "Credit Card"], ...]
ट्री-शेकिंग निहितार्थ
- यूनियन टाइप्स: स्वाभाविक रूप से ट्री-शेकेबल हैं क्योंकि वे केवल कंपाइल-टाइम हैं।
- कॉन्स्ट असर्शन: जबकि वे एक रनटाइम ऑब्जेक्ट बनाते हैं, आधुनिक बंडलर अक्सर TypeScript के उत्पन्न एनम ऑब्जेक्ट की तुलना में इस ऑब्जेक्ट के अप्रयुक्त गुणों को अधिक प्रभावी ढंग से ट्री-शेक कर सकते हैं। हालांकि, यदि पूरा ऑब्जेक्ट आयात किया जाता है और संदर्भित किया जाता है, तो यह संभवतः शामिल होगा। सावधान मॉड्यूल डिजाइन मदद कर सकता है।
सर्वोत्तम अभ्यास और हाइब्रिड दृष्टिकोण
यह हमेशा "या तो/या" स्थिति नहीं होती है। अक्सर, सबसे अच्छा समाधान एक हाइब्रिड दृष्टिकोण शामिल करता है, खासकर बड़े, अंतर्राष्ट्रीयकृत अनुप्रयोगों में:
- सरल, विशुद्ध रूप से आंतरिक झंडों या पहचानकर्ताओं के लिए जिन्हें कभी भी पुनरावृत्त करने या संबंधित डेटा की आवश्यकता नहीं होती है, यूनियन टाइप्स आम तौर पर सबसे अधिक प्रदर्शनकारी और सबसे साफ विकल्प हैं।
- स्थिरांकों के सेट के लिए जिन्हें पुनरावृत्त करने, यूआई में प्रदर्शित करने, या समृद्ध संबद्ध मेटाडेटा (जैसे लेबल, आइकन, या अनुमतियाँ) की आवश्यकता होती है, कॉन्स्ट असर्शन पैटर्न बेहतर है।
- पठनीयता और स्थानीयकरण के लिए संयोजन: कई टीमें आंतरिक पहचानकर्ताओं के लिए
as constका उपयोग करती हैं और फिर एक अलग अंतर्राष्ट्रीयकरण (i18n) प्रणाली से स्थानीयकृत प्रदर्शन लेबल प्राप्त करती हैं।
// src/constants/order-status.ts
const OrderStatuses = {
PENDING: "PENDING",
PROCESSING: "PROCESSING",
SHIPPED: "SHIPPED",
DELIVERED: "DELIVERED",
CANCELLED: "CANCELLED",
} as const;
type OrderStatus = typeof OrderStatuses[keyof typeof OrderStatuses];
export { OrderStatuses, type OrderStatus };
// src/i18n/en.json
{
"orderStatus": {
"PENDING": "Pending Confirmation",
"PROCESSING": "Processing Order",
"SHIPPED": "Shipped",
"DELIVERED": "Delivered",
"CANCELLED": "Cancelled"
}
}
// src/components/OrderStatusDisplay.tsx
import { OrderStatuses, type OrderStatus } from "../constants/order-status";
import { useTranslation } from "react-i18next"; // Example i18n library
interface OrderStatusDisplayProps {
status: OrderStatus;
}
function OrderStatusDisplay({ status }: OrderStatusDisplayProps) {
const { t } = useTranslation();
const displayLabel = t(`orderStatus.${status}`);
return <span>Status: {displayLabel}</span>;
}
// Usage:
// <OrderStatusDisplay status={OrderStatuses.DELIVERED} />
यह हाइब्रिड दृष्टिकोण as const की प्रकार सुरक्षा और रनटाइम पुनरावृत्ति का लाभ उठाता है, जबकि स्थानीयकृत प्रदर्शन स्ट्रिंग्स को अलग और प्रबंधनीय रखता है, जो वैश्विक अनुप्रयोगों के लिए एक महत्वपूर्ण विचार है।
उन्नत पैटर्न और विचार
बुनियादी उपयोग से परे, यूनियन प्रकारों और कॉन्स्ट असर्शन दोनों को कोड की गुणवत्ता और रखरखाव को और बढ़ाने के लिए अधिक परिष्कृत पैटर्न में एकीकृत किया जा सकता है।
यूनियन टाइप्स के साथ टाइप गार्ड्स का उपयोग करना
यूनियन प्रकारों के साथ काम करते समय, विशेष रूप से जब यूनियन में विविध प्रकार (केवल लिटरल्स नहीं) शामिल होते हैं, तो प्रकारों को संकीर्ण करने के लिए टाइप गार्ड आवश्यक हो जाते हैं। लिटरल यूनियन प्रकारों के साथ, विभेदित यूनियन अपार शक्ति प्रदान करते हैं।
type SuccessEvent = { type: "SUCCESS"; data: any; };
type ErrorEvent = { type: "ERROR"; message: string; code: number; };
type SystemEvent = SuccessEvent | ErrorEvent;
function handleSystemEvent(event: SystemEvent) {
if (event.type === "SUCCESS") {
console.log("Data received:", event.data);
// event is now narrowed to SuccessEvent
} else {
console.log("Error occurred:", event.message, "Code:", event.code);
// event is now narrowed to ErrorEvent
}
}
handleSystemEvent({ type: "SUCCESS", data: { user: "Alice" } });
handleSystemEvent({ type: "ERROR", message: "Network failure", code: 503 });
यह पैटर्न, जिसे अक्सर "विभेदित यूनियन" कहा जाता है, अविश्वसनीय रूप से मजबूत और प्रकार-सुरक्षित है, जो एक सामान्य लिटरल संपत्ति (विभेदक) के आधार पर आपके डेटा की संरचना के बारे में कंपाइल-टाइम गारंटी प्रदान करता है।
Object.values() as const और टाइप असर्शन के साथ
as const पैटर्न का उपयोग करते समय, Object.values() बहुत उपयोगी हो सकता है। हालांकि, Object.values() के लिए TypeScript का डिफ़ॉल्ट अनुमान वांछित से व्यापक हो सकता है (जैसे, लिटरल्स के एक विशिष्ट यूनियन के बजाय string[])। आपको कठोरता के लिए एक प्रकार के असर्शन की आवश्यकता हो सकती है।
const Statuses = {
ACTIVE: "Active",
INACTIVE: "Inactive",
PENDING: "Pending",
} as const;
type StatusValue = typeof Statuses[keyof typeof Statuses]; // "Active" | "Inactive" | "Pending"
// Object.values(Statuses) is inferred as (string | "Active" | "Inactive" | "Pending")[]
// We can assert it more narrowly if needed:
const allStatusValues: StatusValue[] = Object.values(Statuses);
console.log(allStatusValues); // ["Active", "Inactive", "Pending"]
// For a dropdown, you might pair values with labels if they differ
const statusOptions = Object.entries(Statuses).map(([key, value]) => ({
value: key, // Use the key as the actual identifier
label: value // Use the value as the display label
}));
console.log(statusOptions);
/*
[
{ value: "ACTIVE", label: "Active" },
{ value: "INACTIVE", label: "Inactive" },
{ value: "PENDING", label: "Pending" }
]
*/
यह दर्शाता है कि लिटरल प्रकारों को बनाए रखते हुए यूआई तत्वों के लिए उपयुक्त मानों की एक दृढ़ता से टाइप की गई सरणी कैसे प्राप्त करें।
अंतर्राष्ट्रीयकरण (i18n) और स्थानीयकृत लेबल
वैश्विक अनुप्रयोगों के लिए, स्थानीयकृत स्ट्रिंग्स का प्रबंधन सर्वोपरि है। जबकि TypeScript एनम और उनके विकल्प आंतरिक पहचानकर्ता प्रदान करते हैं, प्रदर्शन लेबल को अक्सर i18n के लिए अलग करने की आवश्यकता होती है। as const पैटर्न i18n सिस्टम को खूबसूरती से पूरक करता है।
आप as const का उपयोग करके अपने आंतरिक, अपरिवर्तनीय पहचानकर्ताओं को परिभाषित करते हैं। ये पहचानकर्ता सभी लोकेल में सुसंगत हैं और आपकी अनुवाद फ़ाइलों के लिए कुंजी के रूप में काम करते हैं। वास्तविक प्रदर्शन स्ट्रिंग्स को फिर उपयोगकर्ता की चयनित भाषा के आधार पर एक i18n लाइब्रेरी (जैसे, react-i18next, vue-i18n, FormatJS) से प्राप्त किया जाता है।
// app/features/product/constants.ts
export const ProductCategories = {
ELECTRONICS: "ELECTRONICS",
APPAREL: "APPAREL",
HOME_GOODS: "HOME_GOODS",
BOOKS: "BOOKS",
} as const;
export type ProductCategory = typeof ProductCategories[keyof typeof ProductCategories];
// app/i18n/locales/en.json
{
"productCategories": {
"ELECTRONICS": "Electronics",
"APPAREL": "Apparel & Accessories",
"HOME_GOODS": "Home Goods",
"BOOKS": "Books"
}
}
// app/i18n/locales/es.json
{
"productCategories": {
"ELECTRONICS": "Electrónica",
"APPAREL": "Ropa y Accesorios",
"HOME_GOODS": "Artículos para el hogar",
"BOOKS": "Libros"
}
}
// app/components/ProductCategorySelector.tsx
import { ProductCategories, type ProductCategory } from "../features/product/constants";
import { useTranslation } from "react-i18next";
function ProductCategorySelector() {
const { t } = useTranslation();
return (
<select>
{Object.values(ProductCategories).map(categoryKey => (
<option key={categoryKey} value={categoryKey}>
{t(`productCategories.${categoryKey}`)}
</option>
))}
</select>
);
}
चिंताओं का यह पृथक्करण स्केलेबल, वैश्विक अनुप्रयोगों के लिए महत्वपूर्ण है। TypeScript प्रकार यह सुनिश्चित करते हैं कि आप हमेशा मान्य कुंजियों का उपयोग कर रहे हैं, और i18n प्रणाली उपयोगकर्ता के लोकेल के आधार पर प्रस्तुति परत को संभालती है। यह आपके कोर एप्लिकेशन लॉजिक के भीतर सीधे भाषा-निर्भर स्ट्रिंग्स को एम्बेड करने से बचाता है, जो अंतर्राष्ट्रीय टीमों के लिए एक सामान्य एंटी-पैटर्न है।
निष्कर्ष: अपने TypeScript डिजाइन विकल्पों को सशक्त बनाना
जैसे-जैसे TypeScript विकसित होता जा रहा है और दुनिया भर के डेवलपर्स को अधिक मजबूत और स्केलेबल एप्लिकेशन बनाने के लिए सशक्त बना रहा है, इसकी सूक्ष्म विशेषताओं और विकल्पों को समझना और भी महत्वपूर्ण हो जाता है। जबकि TypeScript का enum कीवर्ड नामित स्थिरांक को परिभाषित करने का एक सुविधाजनक तरीका प्रदान करता है, इसका रनटाइम फुटप्रिंट, ट्री-शेकिंग सीमाएं, और रिवर्स मैपिंग जटिलताएं अक्सर प्रदर्शन-संवेदनशील या बड़े पैमाने पर परियोजनाओं के लिए आधुनिक विकल्पों को अधिक आकर्षक बनाती हैं।
स्ट्रिंग/न्यूमेरिक लिटरल्स के साथ यूनियन टाइप्स सबसे दुबले और सबसे अधिक कंपाइल-टाइम-केंद्रित समाधान के रूप में सामने आते हैं। वे रनटाइम पर कोई जावास्क्रिप्ट उत्पन्न किए बिना असंगत प्रकार की सुरक्षा प्रदान करते हैं, जिससे वे उन परिदृश्यों के लिए आदर्श बन जाते हैं जहां न्यूनतम बंडल आकार और अधिकतम ट्री-शेकिंग प्राथमिकताएं हैं, और रनटाइम गणना एक चिंता का विषय नहीं है।
दूसरी ओर, typeof और keyof के साथ संयुक्त कॉन्स्ट असर्शन (as const) एक अत्यधिक लचीला और शक्तिशाली पैटर्न प्रदान करते हैं। वे आपके स्थिरांक के लिए सत्य का एक एकल स्रोत, मजबूत कंपाइल-टाइम प्रकार सुरक्षा, और रनटाइम पर मानों पर पुनरावृत्ति करने की महत्वपूर्ण क्षमता प्रदान करते हैं। यह दृष्टिकोण विशेष रूप से उन स्थितियों के लिए अच्छी तरह से अनुकूल है जहां आपको अपने स्थिरांक के साथ अतिरिक्त डेटा को संबद्ध करने, गतिशील यूआई को पॉप्युलेट करने, या अंतर्राष्ट्रीयकरण प्रणालियों के साथ सहजता से एकीकृत करने की आवश्यकता होती है।
ट्रेड-ऑफ - रनटाइम फुटप्रिंट, पुनरावृत्ति की जरूरतें, और संबंधित डेटा की जटिलता - पर ध्यान से विचार करके, आप सूचित निर्णय ले सकते हैं जो स्वच्छ, अधिक कुशल और अधिक रखरखाव योग्य TypeScript कोड की ओर ले जाते हैं। इन विकल्पों को अपनाना केवल "आधुनिक" TypeScript लिखने के बारे में नहीं है; यह जानबूझकर वास्तुशिल्प विकल्प बनाने के बारे में है जो आपके एप्लिकेशन के प्रदर्शन, डेवलपर अनुभव और वैश्विक दर्शकों के लिए दीर्घकालिक स्थिरता को बढ़ाते हैं।
अपने TypeScript विकास को सही काम के लिए सही उपकरण चुनकर सशक्त बनाएं, जब बेहतर विकल्प मौजूद हों तो डिफ़ॉल्ट एनम से आगे बढ़ें।