Unaandika aina (type) inayopitia vitu vilivyojificha (nested objects), ikijenga njia zinazotenganishwa na nukta kwa ajili ya autocomplete. Inafanya kazi vizuri kwenye kitu kidogo cha majaribio. Kisha unaielekeza kwenye payload halisi ya API, na mhariri (editor) unaganda. Hatimaye, TypeScript inatoa kosa TS2589: Type instantiation is excessively deep and possibly infinite.
Ujumbe huu haumaanishi kuwa kodi yako ina mzunguko usio na mwisho (infinite loop) katika maana ya kawaida. Inamaanisha kuwa compiler kumelewa. Aina uliyoiomba ikokotoe ilikuwa ama haina kikomo kabisa, au ilikuwa na mwisho lakini ni kubwa sana kiasi kwamba kuifanyia tathmini kungezidi mipaka ya ndani ya TypeScript. Hii inapotokea, compiler inasimama kabla haijagandisha IDE yako.
Wakati TS2589 Inapotokea
Aina zinazojirudia (Recursive types) ndizo chanzo kikuu cha tatizo hili. TypeScript hufanya tathmini ya aina kwa haraka, na ikiwa aina ya huduma (utility type) inaendelea kujiita yenyewe—hasa kupitia mantiki ya masharti—mfululizo wa ukokotoaji (computation stack) hukua kwa haraka. Kwa kawaida utakutana na ukuta huu katika mazingira machache mahususi:
- Aina za masharti zinazojirudia (Recursive conditional types) ambazo zinavunja (destructure) tuple, object, au template ya string mara kwa mara hadi kufikia hali ya msingi (base case)
- Wazalishaji wa njia za vitu vilivyojificha sana (Deeply nested object path generators), ambayo yanageuza miundo kama
{ user: { address: { street: string } } }kuwa muunganiko (unions) wa maandishi kama"user" | "user.address" | "user.address.street" - Aina za template literal (Template literal types) ambazo huchambua maandishi herufi kwa herufi au token kwa token
- Aina zilizoorodheshwa (Mapped types) zinazozunguka kwenye vitu vyenye mamia ya funguo (keys) na viwango vingi
- Aina za masharti (Conditional types) ambazo zinasambaza kazi kwenye muunganiko (unions) mkubwa, zikiongeza mzigo wa kazi kimyakimya kwenye kila mwanachama
Mfano wa njia zilizojificha (nested path) ni wa kuvutia sana. Maktaba za fomu (form libraries) na zana za usimamizi wa hali (state-management tools) hupenda kutoa njia zenye aina (typed paths) ili upate autocomplete kwa majina ya nyanja (field names). Kwenye kitu chepesi, kuzalisha kila njia ya nukta kama muunganiko wa maandishi ni jambo rahisi. Kwenye kitu chenye kina kirefu au upana mkubwa, muunganiko huo unalipuka. TypeScript lazima ishike kila mabadiliko (permutation) kwenye kumbukumbu ya kazi kwa wakati mmoja. Katika kina fulani, compiler inagundua kuwa kazi inazidi bajeti yake na inatumia breki ya dharura.
Suluhisho la Kwanza: Ongeza Kikomo cha Kina (Hard Depth Limit)
Njia ya moja kwa moja zaidi ya kutatua TS2589 ni kuacha kujifanya kwamba aina yako inaweza kujirudia milele. Ingiza kiongezi cha kina (depth counter) kinachofanya kazi kama breki ya mzunguko (circuit breaker).
Kiutendaji, hii inamaanisha kuongeza parameter ya jumla ya nambari (numeric generic parameter)—mara nyingi inayowakilishwa kama tuple ambayo urefu wake unashuka—ambayo inapungua kila wakati aina inapojirudia. Kiongezi kinapofikia sifuri, aina inarudisha chaguo la jumla kama string badala ya kuchimba zaidi. Watumiaji bado wanapata autocomplete sahihi kwa viwango vinne au vitano vya kwanza, ambavyo vinashughulikia sehemu kubwa ya vitu vya ulimwengu halisi. Zaidi ya hapo, compiler inapanua tu aina hiyo na kuendelea.
Mtazamo huu haufanyi aina yako ya huduma (utility type) kuwa isiyo sahihi kwa namna yoyote muhimu. Inafanya iwe na kikomo. Mfumo wa aina (type system) unaozima compiler si muhimu zaidi kuliko ule unaokubali kikomo baada ya kina kinachofaa.
Suluhisho la Pili: Thibitisha Njia Moja kwa Wakati
Ikiwa kuzalisha kila njia inayowezekana mapema ni gharama kubwa sana, badilisha mkataba. Badala ya kutoa muunganiko mkubwa wa maandishi yote sahihi, andika aina inayokagua ikiwa maandishi moja mahususi ni njia sahihi.
Fikiria tofauti kati ya kutengeneza kamusi ya kila neno la Kiingereza dhidi ya kukagua ikiwa neno moja limeandikwa kwa usahihi. Ile ya kwanza ni muundo mkubwa wa data; ile ya pili ni uchunguzi mwepesi. Katika istilahi za TypeScript, badala ya ku-export utility ya Paths<T> inayotoa "user.address.street" | "user.settings.theme" | ..., una-export kitu kama IsValidPath<T, "user.address.street">. Compiler inafanya tathmini ya njia unayopitisha tu.
Mabadiliko haya yanabadilisha jinsi unavyounda API. Saini za kazi zako (function signatures) zinaweza kupokea string na kisha kutumia kizuizi cha jumla (generic constraint) ili kuithibitisha dhidi ya umbo la kitu (object shape). IDE bado inalalamika ikiwa mwanatengenezaji ataandika njia mbaya, lakini compiler haitakiwi kamwe kuunda seti kamili ya njia halali wakati wa ukaguzi wa aina (type-checking). Kwa vitu vikubwa, tofauti ya utendaji ni kubwa sana.
Mbinu za Haraka Zinazokusaidia Kuendelea
Zaidi ya marekebisho hayo mawili ya kimuundo, tabia chache ndogo zinaweza kuzuia aina zinazojirudia zisivuke mipaka:
Funga vigezo vya aina (type parameters) kwenye tuples ili kuzuia usambazaji. Vigezo vya aina visivyo na ulinzi katika hali ya masharti (conditional), kama vile
T extends Foo ? Bar : Baz, husambaza ukaguzi kwenye kila mwanachama wakatiTni union. Ikiwa union hiyo ina washiriki hamsini, TypeScript hufanya uundaji (instantiations) hamsini tofauti. Kuandika[T] extends [Foo] ? Bar : Bazhukagua hali hiyo mara moja dhidi ya union nzima. Tumia njia hii kila unapohitaji aina hiyo isipokuwa kama unahitaji kuipitia kila mwanachama wa union mmoja mmoja.Punguza viingilio vyako wakati wa kurekebisha makosa (debugging). TS2589 inapotokea, badilisha aina ya kitu (object type) cha uzalishaji na mfano mdogo (stub) wenye sifa mbili na tabaka moja la nesting. Ikiwa kosa litatoweka, umethibitisha kuwa kina (depth) au idadi (cardinality) ndiyo tatizo, na si kosa la sintaksi. Hii inakuokoa usijirekebishe upya mantiki ambayo ilikuwa sawa kistrukti.
Lainisha aina za API zinazoonekana kwa watumiaji. Ndani ya mfumo, unaweza kuhitaji usahihi wa hali ya juu. Kwa nje, ukamilifu wakati mwingine unagharimu zaidi kuliko unavyotoa faida. Ikiwa aina ya autocomplete iliyopanuliwa kidogo itazuia ucheleweshaji wa sekunde mbili kwenye edita, biashara hiyo kwa kawaida inastahili. Unaweza kuunganisha aina hiyo iliyolegea na kivinjari cha wakati wa utendaji (runtime validator) ili kukamata njia mbaya wakati wa majaribio.
Kwa nini TypeScript Inalazimisha Mpaka Huu
TypeScript haiwezi kutatua tatizo la kusimama (halting problem). Haijui ikiwa aina yako ya mzunguko (recursive type) hatimaye itakoma au itazunguka milele. Badala ya kujihatarisha kwa mzunguko usio na mwisho ndani ya kileo (compiler), inalazimisha kikomo cha tahadhari. Wakati mwingine kikomo hicho hukamata aina ambayo ingekuwa imemaliza, ikipewa muda wa kutosha. TS2589 ni kileo kukiri kwamba ingependelea kuwa salama kuliko kujuta.
Kuheshimu kikomo hicho ni sehemu ya kuandika aina za kiwango cha uzalishaji (production-grade types). Ufafanuzi wa aina ni kodi inayofanya kazi kwenye kileo, na kodi yenye gharama kubwa ina madhara ya kweli. Slow autocomplete inadhuru kasi ya msanidi programu (developer velocity) sawa na kodi ya runtime inayochelewa inavyodhuru uzoefu wa mtumiaji.
Funzo Halisi
TS2589 si ishara kwamba wewe ni mpangaji mbaya wa mifumo ya aina (type-system programmer). Ni ishara kwamba aina yako inafanya kazi nyingi kwa wakati mmoja. Weka kikomo kwenye mzunguko wako (recursion), thibitisha kwa uvivu (validate lazily), na linda dhidi ya usambazaji usio wa lazima. Lengo la aina za hali ya juu si kuthibitisha kila ukweli unaowezekana wakati wa kuunganisha (compile time); ni kuipa timu yako zana za haraka na zinazoaminika. Aina inayokamilika (compiles) kwa milisekunde chache na inayofunika asilimia tisini na tano ya matukio ina thamani kubwa zaidi kuliko ile ambayo ni kamilifu kiteoriamu lakini inasababisha language server kuzama (crash).
