તમે એક એવો type લખો છો જે નેસ્ટેડ ઓબ્જેક્ટ્સમાં ફરે છે અને ઓટોકમ્પ્લીટ માટે ડોટ-સેપરેટેડ (dot-separated) પાથ બનાવે છે. તે એક નાના ટેસ્ટ ઓબ્જેક્ટ પર સુંદર રીતે કામ કરે છે. પછી તમે તેને વાસ્તવિક API પેલોડ પર લગાવો છો, અને એડિટર ફ્રીઝ થઈ જાય છે. અંતે, TypeScript એરર TS2589 આપે છે: Type instantiation is excessively deep and possibly infinite.
આ સંદેશનો અર્થ એ નથી કે તમારા કોડમાં પરંપરાગત અર્થમાં ઇન્ફિનિટ લૂપ (infinite loop) છે. તેનો અર્થ એ છે કે કમ્પાઇલરે હાર માની લીધી છે. તમે જે type ની ગણતરી કરવા કહ્યું હતું તે કાં તો ખરેખર અનલિમિટેડ (unbounded) હતું, અથવા મર્યાદિત હતું પણ એટલું મોટું હતું કે તેનું મૂલ્યાંકન કરવાથી TypeScript ની આંતરિક મર્યાદાઓ પૂરી થઈ જાય. જ્યારે આવું થાય છે, ત્યારે તમારું IDE હેંગ ન થાય તે માટે કમ્પાઇલર અટકી જાય છે.
જ્યારે TS2589 દેખાય ત્યારે
રિકર્સિવ ટાઇપ્સ (Recursive types) એ સૌથી સામાન્ય કારણ છે. TypeScript ટાઇપ્સનું ઝડપથી મૂલ્યાંકન કરે છે, અને જો કોઈ યુટિલિટી ટાઇપ પોતાને વારંવાર કોલ કરતું રહે—ખાસ કરીને કન્ડિશનલ લોજિક દ્વારા—તો કમ્પ્યુટેશન સ્ટેક ઝડપથી વધે છે. તમે સામાન્ય રીતે આ સમસ્યા નીચેના ચોક્કસ કિસ્સાઓમાં અનુભવશો:
- રિકર્સિવ કન્ડિશનલ ટાઇપ્સ જે બેઝ કેસ (base case) સુધી પહોંચવા માટે વારંવાર ટ્યુપલ (tuple), ઓબ્જેક્ટ અથવા સ્ટ્રિંગ ટેમ્પલેટને ડિસ્ટ્રક્ચર કરે છે
- ડીપલી નેસ્ટેડ ઓબ્જેક્ટ પાથ જનરેટર્સ, જે
{ user: { address: { street: string } } }જેવી સ્ટ્રક્ચર્સને"user" | "user.address" | "user.address.street"જેવા સ્ટ્રિંગ લિટરલ્સના યુનિયન્સમાં ફેરવે છે - ટેમ્પલેટ લિટરલ ટાઇપ્સ જે સ્ટ્રિંગ્સને કેરેક્ટર બાય કેરેક્ટર અથવા ટોકન બાય ટોકન પાર્સ કરે છે
- મેપ્ડ ટાઇપ્સ જે ડઝનબંધ કીઝ અને મલ્ટિપલ લેવલ ધરાવતા ઓબ્જેક્ટ્સ પર ઇટરેટ થાય છે
- કન્ડિશનલ ટાઇપ્સ જે મોટા યુનિયન્સ પર ડિસ્ટ્રીબ્યુટ થાય છે, જેનાથી દરેક મેમ્બર પર કામનો ભાર ગુણાકારમાં વધી જાય છે
નેસ્ટેડ પાથનું ઉદાહરણ ખાસ કરીને આકર્ષક છે. ફોર્મ લાઇબ્રેરીઓ અને સ્ટેટ-મેનેજમેન્ટ ટૂલ્સ ફિલ્ડ નામો માટે ઓટોકમ્પ્લીટ મળે તે માટે ટાઇપ્ડ પાથ ઓફર કરવાનું પસંદ કરે છે. છીછરા (shallow) ઓબ્જેક્ટ પર, દરેક કાયદેસરના ડોટ-પાથને સ્ટ્રિંગ યુનિયન તરીકે જનરેટ કરવો સરળ છે. પરંતુ ઊંડા અથવા વિશાળ ઓબ્જેક્ટ પર, તે યુનિયન વિસ્ફોટક રીતે વધી જાય છે. TypeScript એ દરેક પરમ્યુટેશનને એકસાથે વર્કિંગ મેમરીમાં રાખવું પડે છે. અમુક ઊંડાઈએ, કમ્પાઇલર નોંધે છે કે કામ તેના બજેટ કરતા વધી રહ્યું છે અને તે ઇમરજન્સી બ્રેક લગાવી દે છે.
ઉકેલ એક: મર્યાદિત ડેપ્થ લિમિટ (Hard Depth Limit) ઉમેરો
TS2589 ને ઉકેલવાનો સૌથી સીધો રસ્તો એ છે કે તમારો type અનંતકાળ સુધી રિકર્સ થઈ શકે તેવું માનવાનું બંધ કરો. એક ડેપ્થ કાઉન્ટર (depth counter) દાખલ કરો જે સર્કિટ બ્રેકર તરીકે કામ કરે.
વ્યવહારમાં, આનો અર્થ એ છે કે એક ન્યુમેરિક જનરિક પેરામીટર ઉમેરવો—જેને ઘણીવાર ટ્યુપલ તરીકે દર્શાવવામાં આવે છે જેની લંબાઈ ઘટતી જાય છે—જે દરેક વખતે type રિકર્સ થાય ત્યારે ઘટે છે. જ્યારે કાઉન્ટર શૂન્ય પર પહોંચે, ત્યારે type વધુ ઊંડાણ સુધી જવાને બદલે string જેવો વ્યાપક ફોલબેક (fallback) રિટર્ન કરે છે. વપરાશકર્તાઓને પ્રથમ ચાર કે પાંચ લેવલ માટે સચોટ ઓટોકમ્પ્લીટ મળે છે, જે વાસ્તવિક દુનિયાના મોટાભાગના ઓબ્જેક્ટ્સને આવરી લે છે. તેનાથી આગળ, કમ્પાઇલર ફક્ત type ને વ્યાપક (widen) બનાવે છે અને આગળ વધે છે.
આ અભિગમ તમારા યુટિલિટી ટાઇપને કોઈ અર્થપૂર્ણ રીતે ઓછો સાચો બનાવતો નથી. તે તેને મર્યાદિત (bounded) બનાવે છે. જે ટાઇપ સિસ્ટમ કમ્પાઇલરને ક્રેશ કરે છે તે સમજદારીપૂર્વક મર્યાદા સ્વીકારી લે તેવી સિસ્ટમ કરતા વધુ ઉપયોગી નથી.
ઉકેલ બે: એક સમયે એક જ પાથ વેલિડેટ કરો
જો દરેક શક્ય પાથ અગાઉથી જનરેટ કરવો ખૂબ મોંઘો પડતો હોય, તો કરાર (contract) બદલો. તમામ માન્ય સ્ટ્રિંગ્સનું વિશાળ યુનિયન બનાવને બદલે, એવો type લખો જે તપાસે કે એક ચોક્કસ સ્ટ્રિંગ માન્ય પાથ છે કે નહીં.
દરેક અંગ્રેજી શબ્દની ડિક્શનરી બનાવવી અને કોઈ એક શબ્દની જોડણી સાચી છે કે નહીં તે તપાસવા વચ્ચેના તફાવત વિશે વિચારો. પહેલું એક વિશાળ ડેટા સ્ટ્રક્ચર છે; બીજું એક હળવું સ્કેનિંગ છે. TypeScript ના સંદર્ભમાં, "user.address.street" | "user.settings.theme" | ... આપતું Paths<T> યુટિલિટી એક્સપોર્ટ કરવાને બદલે, IsValidPath<T, "user.address.street"> જેવું કંઈક એક્સપોર્ટ કરો. કમ્પાઇલર ફક્ત તે જ પાથનું મૂલ્યાંકન કરે છે જે તમે ખરેખર પાસ કરો છો.
આ ફેરફાર તમે API કેવી રીતે ડિઝાઇન કરો છો તે બદલી નાખે છે. તમારા ફંક્શન સિગ્નેચર કદાચ એક સ્ટ્રિંગ સ્વીકારી શકે છે અને પછી ઓબ્જેક્ટ શેપ સામે તેને વેરિફાય કરવા માટે જનરિક કન્સ્ટ્રેઇન્ટનો ઉપયોગ કરી શકે છે. જો ડેવલપર ખોટો પાથ ટાઇપ કરે તો IDE હજુ પણ ફરિયાદ કરશે, પરંતુ ટાઇપ-ચેકિંગ દરમિયાન કમ્પાઇલરે ક્યારેય કાયદેસરના પાથનો સંપૂર્ણ સેટ બનાવવો પડશે નહીં. મોટા ઓબ્જેક્ટ્સ માટે, પરફોર્મન્સમાં તફાવત ઘણો મોટો હોય છે.
ઝડપી યુક્તિઓ જે તમને આગળ વધવામાં મદદ કરશે
આ બે સ્ટ્રક્ચરલ ફિક્સ સિવાય, કેટલીક નાની આદતો રિકર્સિવ ટાઇપ્સને મર્યાદા ઓળંગતા અટકાવી શકે છે:
ડિસ્ટ્રિબ્યુશનને રોકવા માટે ટાઇપ પેરામીટર્સને ટ્યુપલ્સમાં (tuples) લપેટી દો. જ્યારે
Tએક યુનિયન (union) હોય, ત્યારેT extends Foo ? Bar : Bazજેવી કન્ડિશનલમાં રહેલો નગ્ન (naked) ટાઇપ પેરામીટર દરેક મેમ્બર પર ચેક વહેંચી દે છે. જો તે યુનિયનમાં પચાસ મેમ્બર હોય, તો TypeScript પચાસ અલગ-અલગ ઇન્સ્ટન્શિએશન (instantiations) કરે છે.[T] extends [Foo] ? Bar : Bazલખવાથી આખી યુનિયન સામે કન્ડિશનલનું એક જ વાર મૂલ્યાંકન થાય છે. જ્યારે તમને ખરેખર દરેક યુનિયન મેમ્બર પર વ્યક્તિગત રીતે મેપિંગ કરવાની જરૂર ન હોય, ત્યારે આનો ઉપયોગ કરો.ડિબગિંગ કરતી વખતે તમારા ઇનપુટ્સ ઘટાડો. જ્યારે TS2589 દેખાય, ત્યારે તમારા પ્રોડક્શન ઓબ્જેક્ટ ટાઇપને બે પ્રોપર્ટીઝ અને એક લેવલ નેસ્ટિંગ ધરાવતા નાના સ્ટબ (stub) સાથે બદલી નાખો. જો ભૂલ દૂર થઈ જાય, તો તમે સાબિત કરી દીધું કે સમસ્યા ડેપ્થ (depth) અથવા કાર્ડિનાલિટી (cardinality) માં છે, સિન્ટેક્સની ભૂલ નથી. આ તમને એવા લોજિકને ફરીથી લખવાથી બચાવે છે જે વાસ્તવમાં સ્ટ્રક્ચરલ રીતે બરાબર હતું.
પબ્લિક-ફેસિંગ API ટાઇપ્સને થોડી લવચીક બનાવો. આંતરિક રીતે, તમને અત્યંત ચોકસાઈની જરૂર પડી શકે છે. બાહ્ય રીતે, ક્યારેક સંપૂર્ણતા (perfection) તેના ફાયદા કરતા વધુ ખર્ચાળ સાબિત થાય છે. જો થોડો વ્યાપક (wider) ઓટોકમ્પ્લીટ ટાઇપ એડિટરમાં બે સેકન્ડનો લેગ (lag) અટકાવે છે, તો તે બદલાવ સામાન્ય રીતે ફાયદાકારક છે. તમે ટેસ્ટિંગમાં ખોટા પાથ પકડવા માટે આ લૂઝ ટાઇપને રનટાઇમ વેલિડેટર સાથે જોડી શકો છો.
TypeScript આ મર્યાદા શા માટે લાદે છે
TypeScript 'halting problem' ઉકેલી શકતું નથી. તેને ખબર નથી હોતી કે તમારો રિકર્સિવ (recursive) ટાઇપ અંતે સમાપ્ત થશે કે અનંત સમય સુધી ચાલતો રહેશે. કમ્પાઈલરની અંદર ઇન્ફિનિટ લૂપનું જોખમ લેવાને બદલે, તે એક સાવચેતીભર્યું કટઓફ (cutoff) લાદે છે. ક્યારેક તે કટઓફ એવા ટાઇપને પકડી લે છે જે પૂરતો સમય આપતા પૂરો થઈ શકત. TS2589 એ કમ્પાઈલર દ્વારા સ્વીકૃતિ છે કે તે ભૂલ કરવા કરતા સુરક્ષિત રહેવાનું પસંદ કરે છે.
તે મર્યાદાનું પાલન કરવું એ પ્રોડક્શન-ગ્રેડ ટાઇપ્સ લખવાનો એક ભાગ છે. ટાઇપ ડેફિનેશન એ કમ્પાઈલરમાં ચાલતો કોડ છે, અને મોંઘો (expensive) કોડ વાસ્તવિક પરિણામો લાવે છે. ધીમો ઓટોકમ્પ્લીટ ડેવલપરની કાર્યક્ષમતાને એટલી જ નુકસાન પહોંચાડે છે જેટલું ધીમો રનટાઇમ કોડ યુઝર એક્સપિરિયન્સને નુકસાન પહોંચાડે છે.
મુખ્ય નિષ્કર્ષ
TS2589 એ સંકેત નથી કે તમે ખરાબ ટાઇપ-સિસ્ટમ પ્રોગ્રામર છો. તે સંકેત છે કે તમારો ટાઇપ એકસાથે ઘણું બધું કામ કરી રહ્યો છે. તમારા રિકર્ઝનને મર્યાદિત કરો, લેઝી (lazily) વેલિડેશન કરો અને બિનજરૂરી ડિસ્ટ્રિબ્યુશન સામે રક્ષણ કરો. એડવાન્સ્ડ ટાઇપ્સનો ધ્યેય કમ્પાઈલ ટાઈમ પર દરેક શક્ય સત્ય સાબિત કરવાનો નથી; તેનો ધ્યેય તમારી ટીમને ઝડપી અને વિશ્વસનીય ટૂલિંગ આપવાનો છે. જે ટાઇપ મિલિસેકન્ડ્સમાં કમ્પાઈલ થાય છે અને નવ્વાણું ટકા કેસોને આવરી લે છે તે સૈદ્ધાંતિક રીતે સંપૂર્ણ હોવા છતાં લેંગ્વેજ સર્વરને ક્રેશ કરી દે તેવા ટાઇપ કરતા ઘણો વધુ મૂલ્યવાન છે.
