- TypeScript में runtime type information emit करने की आवश्यकता का तर्क दिया गया है, और इस समस्या को बायपास करने वाले Type Mapping, Code Generation / External Tool, तथा Adapter प्रोजेक्ट्स की एक सूची संकलित की गई है
- मुख्य समस्या यह है कि reflective type system के बिना serialization और validation को संभालने पर अंतहीन boilerplate या schema file-आधारित bespoke code generation की ज़रूरत पड़ती है
- workaround के रूप में io-ts, zod आदि पेश किए जाते हैं, लेकिन types को हर library के तरीके से फिर से declare करना पड़ता है और libraries TypeScript की सभी type features को support नहीं कर पातीं, जिससे असुविधा होती है
- यह माना गया है कि TypeScript का type erasure इस लिहाज़ से फ़ायदेमंद है कि JavaScript projects, TypeScript की जानकारी के बिना emit किए गए JavaScript का उपयोग कर सकते हैं, फिर भी यह तर्क दिया गया है कि code से अलग lookup table के रूप में type information emit की जा सकती है
- decorators से इसे हल न करने का अनुरोध किया गया है, और interface के उपयोग तथा external library types के support के लिए
typescript.generateRuntimeType<T>()जैसी compiler-recognized higher-order function, F# Type Providers, और C# Source Generators जैसे approaches सुझाए गए हैं - संबंधित मौजूदा चर्चाओं में 8 साल पुराना GitHub issue जोड़ा गया है, और अनुरोध किया गया है कि अगर किसी project ने यही समस्या झेली है तो approach की संख्या की परवाह किए बिना सूची में जोड़ने के लिए PR भेजें
1 टिप्पणियां
Hacker News टिप्पणियाँ
यह समझने के लिए कि माँगा क्या जा रहा है, मुझे समस्या विवरण लगभग 4 बार पढ़ना पड़ा, और यह वही तरह का मामला है जो दिखाता है कि संक्षिप्त लेखन क्यों महत्वपूर्ण है
असली मांग runtime type safety की लगती है, लेकिन TypeScript लंबे समय से यह रेखा खींचता आया है कि वह JavaScript का replacement/augmented runtime नहीं बनेगा, इसलिए इसकी संभावना कम लगती है। TypeScript की भूमिका JS में compile होकर हट जाने की है, और उसके बाद V8 आदि में जो होता है वह उसके दायरे से बाहर है।
“TypeScript को runtime type information emit करनी चाहिए” जैसी मांग, मौजूदा TypeScript से काफ़ी अलग एक नया product मांगने के ज़्यादा करीब है
उदाहरण के लिए, कोई generic
validatefunction हो जो किसी भी interface और object को लेकर validation कर सके। Compile-time reflection से type-specific JS validation code generate करना, या runtime परTको argument के रूप में देकर compare करना संभव हो सकता है, लेकिन मौजूदा TypeScript philosophy में यह कठिन लगता हैTypeScript compilation के दौरान बहुत-सी type information जानता है, लेकिन compile खत्म होने पर उसे फेंक देता है। इस जानकारी को file में emit किया जा सकता है या
Reflectपर metadata के रूप में छोड़ा जा सकता है, फिर भी इसे त्याग देना खासकर JavaScript की “everything is an object” प्रकृति को देखते हुए एक अफ़सोसजनक सीमा है।भले ही पूरी TS type safety न मिले, getter/setter या ES2015
Proxyसे कुछ checks जोड़े जा सकते हैं, और अगर type information runtime में बची रहे तो executable metadata के रूप में कई दिलचस्प संभावनाएँ खुलती हैंTS
enumपहले से JS object के रूप में output होता है, इसलिए runtime query संभव है, लेकिन string literal union type के साथ ऐसा नहीं है। TypeScript type guard functions को भी support करता है, इसलिए type system की जानकारी से ऐसी functions auto-generate करना भी बहुत कठिन नहीं लगताइसे PDB file जैसी उपमा से समझा जा सकता है। यह वह जानकारी है जो TypeScript के पास पहले से होती है और बाद में फेंक दी जाती है, इसलिए इसके लिए पूरी तरह नया product ज़रूरी नहीं है
https://github.com/microsoft/TypeScript/issues/3628
TypeScript PM के नज़रिए से इस इच्छा को समझा जा सकता है। Data validation में अक्सर runtime type checking की ज़रूरत पड़ती है, और उस कमी को भरने के लिए कई libraries भी हैं
लेकिन यह तथ्य कि अलग-अलग design decisions वाली इतनी libraries मौजूद हैं, खुद इस बात का संकेत है कि यह कोई ऐसा solved problem नहीं है जिसका एक स्पष्ट सही उत्तर हो। TypeScript के शुरुआती design के समय भी यह बात ज्ञात थी, और लगता है कि यह सिद्धांत काफ़ी अच्छी तरह कायम रहा है।
इसके बजाय TypeScript इतना शक्तिशाली हो गया है कि runtime type checking libraries जो वास्तव में करती हैं, उसे types में काफ़ी सटीक रूप से व्यक्त कर सके, और users APIs के ज़रिए types से runtime validation logic बना सकते हैं। यह एक उचित स्तर की flexibility लगता है
उदाहरण के लिए, किसी निश्चित range के भीतर number या postal code pattern को satisfy करने वाली string को type के रूप में define किया जाए, और compiler validity जाँचने के लिए सामान्य functions की तरह लिखे गए validation functions का उपयोग करे। मैं सोचता हूँ कि ऐसी feature का न होना क्या अनावश्यक जटिलता से बचने का design decision है, या performance जैसी तकनीकी सीमाओं की वजह से है
अभी भी जिन्हें ज़रूरत है वे
tscको देने से पहले type information से runtime objects generate करने वाला preprocessor बना सकते हैं, लेकिन प्रयास बिखरे हुए हैं। अगर कोई आधिकारिक pluggable preprocessor और ecosystem हो, तो अच्छा समाधान निकल सकता हैबस शुरुआत मिल जाए तो community maintenance में मदद करेगी, और client generation से लेकर runtime type assertions तक कई code generation needs हल हो सकती हैं
Classobjects में छोड़ने वाले दूसरे proposal के बारे में लोग क्या सोचते हैं?इसे न करने की एक वजह है। TypeScript, JavaScript के ऊपर किसी runtime जैसी चीज़ बन जाएगा, और JS में compile होने वाली एक नई भाषा बनकर रह जाएगा
अभी TypeScript, type annotations लगे JavaScript के काफ़ी क़रीब है। JS में compile होने वाली भाषाएँ पहले से बहुत हैं, तो उनमें से कोई एक इस्तेमाल की जा सकती है। जिन्हें runtime types वाला TypeScript चाहिए, वे शायद JavaScript की तुलना में Java/OOP style code लिखना चाहते हैं, लेकिन JavaScript एक dynamic type language है, और यही उसकी एक खूबी भी है
generateTypeInfo!()जैसा कोई macroFootype को encode करने वाले JS object में expand हो, तो वह अब भी पढ़ने योग्य JavaScript में compile होगालेकिन यह macro उस गुण को तोड़ देता है कि “TS = type annotations वाला JS है और compile करने के लिए बस annotations हटाने होते हैं।” क्योंकि इसमें
Fooके structural type की वास्तव में गणना करनी पड़ती है।और TypeScript types structural होते हैं, इसलिए runtime पर object structure की जाँच करके कुछ हद तक type जाना जा सकता है, लेकिन अगर erased nominal information या type name चाहिए हों, जैसे यह जाँचना कि कोई string union में है या नहीं, तो TypeScript type system की Turing-completeness और implicit structural conversion की वजह से यह जल्दी ही कठिन समस्या बन जाती है
गंभीरता से कहें तो, runtime के बिना भी अगर types को data के रूप में expose किया जाए, तो reflection मिल सकता है। इतने सारे developers ने इसकी नकल करने की कोशिश की है कि इसे न करना उल्टा मूर्खता जैसा लगता है
runtime types, compile-time type system और data validation type system को अलग-अलग duplicate बनाए रखने की समस्या हल करते हैं, और इसका OOP से सीधा संबंध नहीं दिखता। इस समस्या के workaround libraries में जिस
io-tsको लोग पसंद करते हैं, वह भी functional programming पर काफ़ी निर्भर हैNaNजैसे अनपेक्षित values पर बार-बार ठोकर खाना एक उलझी हुई आपदा जैसा हैमैंने जिन workflows में देखा है, उनमें TypeScript पहले से JS में compile होने वाली भाषा है, तो बेहतर यही है कि उसके इस फ़ायदे का पूरा इस्तेमाल किया जाए
यह type annotations + Babel के ज़्यादा क़रीब है, और इसमें एक बेहद उपयोगी feature और जोड़ना अच्छा विचार होगा।
JSON.parseकी गई string को typed structure में सुरक्षित रूप से बदल न पाना अजीब है, जबकि दूसरी भाषाओं में यह आम तौर पर संभव हैनीचे की बातों से पहले, यह बहस के लायक एक वैध विषय है और मुझे नहीं लगता कि इसका कोई एक वस्तुनिष्ठ सही जवाब है
TypeScript,
Enumद्वारा object export करने वाले अपवाद को छोड़ दें तो, JavaScript के ऊपर एक optional layer है, और TS code types हटाने पर JS बन जाता है। अगर इस सिद्धांत से बहुत दूर न जाएँ, तो असली माँग type से serializer/validator बनाने वाली library जैसी लगती है। ऐसी libraries पहले से बहुत हैं, इसलिए यह माँग आख़िरकार उनमें से किसी एक को आधिकारिक standard विकल्प बनाने जैसी दिखती है।निजी तौर पर मैं नहीं चाहता कि TypeScript की core language में runtime reflection आए। runtime पर सिर्फ JavaScript हो, और source map न होने पर भी output JS पढ़ने योग्य और debug करने लायक रहे—मैं इस बात को काफ़ी महत्व देता हूँ
Enumके अलावा भी runtime code emit करने वाले अपवाद हैं, और उनमें से ज़्यादातर को भी गलती ही माना जाता है। उदाहरण के लिएmoduleऔरnamespaceruntime structures थेहालाँकि पिछले कुछ वर्षों में उनका इस्तेमाल मुख्यतः TypeScript के अंदर ही हुआ, और TypeScript भी हाल के समय में उनसे दूर जा रहा है। एक और काफ़ी लोकप्रिय अपवाद parameter properties हैं, जो constructor parameters से class member types define करने का syntax है, और क्योंकि यह दोहराया हुआ boilerplate कम करता है, शायद इसलिए इस पर कम विवाद हुआ
TypeScript देवताओं से विनती करने के बजाय, शायद JavaScript पक्ष से सौदा करके type checking को JS में लाना बेहतर होगा
अगर runtime types चाहिए, तो type guards का इस्तेमाल करें, और अगर यह बार-बार चाहिए, तो
io-tsयाzodसे types को validator/codec/schema के रूप में लिखें। जब तक JavaScript में इस पर कोई सहमति वाला तरीका नहीं आता, मुझे नहीं लगता कि TS spec, type checker और community को runtime validation का बोझ उठाना चाहिएzodसे schema और validators लिखकर वहीं से types derive करने वाला तरीका मुझे काफ़ी पसंद है। अच्छा यह है कि validation, type changes के साथ-साथ evolve होती रहती हैकुछ लोगों को लगता है कि ऐसी सुविधा language में होनी चाहिए, लेकिन हर library में कई विवादास्पद design choices होती हैं, इसलिए project की ज़रूरत के हिसाब से implementation चुनना शायद बेहतर है।
लेकिन
zodruntime validation और schema-based type derivation जैसे pattern,ts-patternकी pattern matching के साथ हमेशा अच्छी तरह फिट नहीं बैठते। इन libraries की type definitions किसी भूलभुलैया जैसी हैं, और कभी-कभी जो code चलना चाहिए लगता है, वह उम्मीद के मुताबिक काम नहीं करता। अगर runtime safety और exhaustiveness-checked pattern matching सहज रूप से जुड़ जाएँ, तो वह बहुत अच्छा होगाPromise, decorators, और नया pipe operator—तीनों के implementation direction मुझे ग़लत लगे हैंमुझे याद है कि TypeScript developers में से एक ने कहा था कि अगर फिर से शुरू करना पड़े तो वह
enumनहीं जोड़ते। वजह यह थी किenumही वह एकमात्र feature है जो runtime code emit करता हैTypeScript runtime behavior नहीं बदलता, और उसमें TypeScript-विशेष
{#if}जैसी कोई चीज़ भी नहीं हैफिर भी TypeScript में कुछ features ऐसे हैं जो “JavaScript + type annotations” से आगे जाते हैं। जैसे
namespace, constructor arguments में class properties define करने का syntax, पुराने experimental decorators, औरthisparameter syntax, जो compile होने पर गायब हो जाता है लेकिन JS functions पर सिर्फ type annotations लगाने से ज़्यादा जैसा लगता हैइससे बेहतर शीर्षक शायद “TypeScript, reflection/runtime types दे” जैसा होगा
अभी सबसे अच्छा समाधान शायद
emitDecoratorMetadataलगता है। https://www.typescriptlang.org/tsconfig#emitDecoratorMetadataलेखक को शायद TypeScript के design goals की गलतफहमी है। लक्ष्य बिना complexता वाला साफ़ JS output बनाना नहीं, बल्कि TypeScript की runtime semantics को JavaScript के समान बनाए रखना है
अफसोसनाक अपवाद
enumको छोड़ दें तो TypeScript सिर्फ type annotations हटाने पर JavaScript बन जाता है। TypeScript का पूरा ecosystem पूर्ण type erasure पर निर्भर करता है, और अगर यह मांग मान ली गई तो ESBuild, Deno, और Bun का TS support लगभग असंभव हो सकता है। क्योंकि फिर हर एक को पूरेtscको अपनी-अपनी भाषा में दोबारा implement करना पड़ेगा।दूसरी ओर, OP जिन complex libraries की शिकायत कर रहा है, वे user space में implement की गई हैं, इसलिए ऐसे tools के साथ compatible हैं
keyofको class key list में बदला जा सकता है, या TS classes को ES6 classes की तरह keys कोundefinedसे initialize करने दिया जा सकता हैअभी TypeScript classes सभी ऐसी keys हटा देती हैं जो explicitly define नहीं की गई हैं, इसलिए नए instance पर
Object.keys()call करने पर कुछ भी नहीं निकलता। सिर्फtsconfig.jsonoption के जरिए TS classes को ES6 classes की तरह transform कर दिया जाए, या ES6 classes को TS के अंदर साथ में allow कर दिया जाए, तो code generation बहुत आसान हो जाएगा।साथ ही, अगर
tstypeof Foo::barजैसी static syntax"string"में compile हो जाए, तो वह शानदार होगा। सिर्फ basic RTTI से भी काफी गंदा boilerplate TypeScript code तुरंत बहुत कम हो सकता हैयह लेख अच्छा लगा। मैंने 2018 में TypeScript शुरू किया था और करीब 2 साल बाद मुझे लगने लगा कि यह मूलतः पूरी तरह का समाधान नहीं है
मैं Haskell/C#/F# से आया हूँ, और TS में powerful type system होने के बावजूद, development के कुछ हिस्सों को छोड़कर उन भाषाओं वाले फायदे काफी हद तक नहीं मिलते। real world को handle करते समय यह practical रूप से C# से कहीं ज़्यादा सीमित है। अगर आपको यह बात न पता हो कि compile होने के बाद यह बिना checks के JS बन जाता है, तो leaky abstraction को लगातार ध्यान में रखना पड़ता है ताकि ऐसे bugs से बचा जा सके जो static typing का दावा करने वाले tool में संभव नहीं होने चाहिए
अगर .NET develop करना है तो C# इस्तेमाल करें, और web develop करना है तो TypeScript। C# nominal typing वाला है और उसमें काफ़ी अच्छी reified generics हैं, जबकि TypeScript structural typing वाला है और soundness छोड़ने के बदले complex type relationships को व्यक्त करने वाला शक्तिशाली type system देता है।
दोनों को मिलाकर बनी भाषा अच्छी होगी या नहीं, इस पर संदेह है; दोनों भाषाएँ अपनी-अपनी अलग दिशा में अच्छी तरह काम करती हैं, लेकिन वे दिशाएँ एक-दूसरे से खास मेल नहीं खातीं
runtime data validation के लिए https://zod.dev इस्तेमाल करके मैं पूरी तरह संतुष्ट रहा हूँ
यह काफी अच्छा लगा कि अलग से nominal types define किए बिना, fluent API के साथ वहीं पर intent व्यक्त किया जा सकता है
एक आसान उदाहरण के तौर पर, domain में संख्या
2और मुद्रा राशि2के बीच फर्क करने के लिए इसकी ज़रूरत होती है