اکثریت از فانکشن fetch که مرورگر در دسترستون میذاره استفاده کردید (البته بحثم فقط با اون نیست بلکه با بقیه کتابخونه هایی مثل Axios هم هست)
بذارید کار رو با یک مثال شروع کنیم:
(اگر اون declare براتون جدیده به طور خلاصه اینجوری بخونیدش که داریم به ts میگیم"ما یک url از تایپ string داریم، کار نداشته باش که کجا تعریفش کردیم، فقط هست")
بنظرتون مشکل این تابع چیه؟
این تابع دوتا مشکل اساسی داره یکیش رو میگم فعلا
شاید چیز عجیبی به ذهنتون خطور نکنه چون این اپ واقعا ساده است و واقعا این مشکل ها توی این scale ایرادی نداره، بلکه حتی حل کردنشون میتونه وقت الکی بگیره و complexity رو بیشتر کن.
ولی ازین موضوع بگذریم، درواقع اولین مشکل اون <Promise<Game هست.
کی گفته این تابع <Promise<Game میده چرا <Promise<string نده؟
اگر همین کد روی توی ادیتورتون بزنید بعد تایپ خروجی رو بکنید پرامیس هرچیزی که دلتون میخواد میبینید که هیچ اروری از typescript نمیگیرید
مشکل اصلی اینجاست که خروجی واقعی این فانشکن <Promise<any هستش ( اگر ریترن تایپ فانشکن رو بردارید نشون میده )
نمیدونم چقدر با ts آشنایی دارید و در چه سطحی هستید ولی چند تا سوال ممکنه براتون پیش آمده باشه
اول یه سوال میپرسم ما چرا از ts استفاده میکنیم؟
احتمالا میگید تا type-safety بگیریم، که بتونیم ارور هارو ببریم سمت compile-time تا runtime، که گارانتی نخوردن به ارور در رانتایم رو به حداقل برسونیم و...
خوب این دقیقا داره این type-safety رو از بین میبره، کل دلیل اینکه از ts استفاده میکنیم رو از بین میبره،
وقتی ندونیم تایپ خروجی چیه، ts میخواهیم چی کار؟
من میتونم URL اشتباه بدم و یه چیز اشتباه بگیرم
چیزی که اون URL میده هم میتونه تغییر کنه باز یه چیز اشتباه بگیریم
درسته، شاید بگیم "بیخیال، کی همیچن کاری میکنه و چه زمانی ممکنه همچین اتفاقی بیوفته" و این حرف غلط هم نیست، درسته، یه همچین اپ ساده ای نیازی به راه حل نداره، خیلی جاها راحت اعتماد میکنیم به API ها توی رانتایم مشکل هم خوردیم مهم نیست.
ولی تو scale بالاتر هرچقدر predictability رو بالاتر ببریم بهتر هست
و البته
من فقط حرفم راجب فچ کردن نیست
مشکل ما باید با چیزایی باشه که مسئولیتشون بخش Input در IO هست ولی گارانتی درستی از Input به ما نمیدن
منظورم از "مسئولیتشون بخش Input است" به طور خلاصه (اگر بیخیال اون O که به معنی Output هست بشیم) میشه "هر نوع استفاده/گرفتن چیزی از بیرون اسکوپ اپمون یا بهتره بگیم دنیای بیرون رو Input میگیم"
مثال زیاد هست، گرفتن تاریخ با همین ()new Data، گرفتن عدد رندوم، فچ کردن، گرفتن ورودی ترمینال، گرفتن یه المنت html از مرورگر و...
بذارید یه مثال بزنم
ما یه ربات داریم که خوب، نمیتونه خودش تصمیم بگیره، بهش یسری عملیات میدیم
مشکل اینجاست اگر داخل بسته به جا کنسرو تن ماهی (چیزی که ما مخواهیم)، یک کنسرو آناناس بود (چیزی که واقعیت هست) چی؟ در اون صروت مراحل بعدی مشکل ساز میشن
این مشکل دقیقا به قضیه ما مپ میشه.
خونه runtime ما هست، جایی که بهش اعتماد کامل داریم، یعنی نیازی نیست چک کنیم که آیا داخل خانه آشبزخانه ای هست یا آیا دری هست یا نه
درب خانه، فچ ما هست، وجود درب یه چیز مسلم هست که رانتایم گارانتی میکنه، ولی اینکه اون طرف در چه میتونه بیاد رو نه (درواقع اون طرف درب میتونه Input ما باشه)
اینکه ما چیزی درمورد تن ماهی بودن کنسرو به ربات نگفتیم هم مشکل ما هست، ما میدونیم که کنسرو ماهی میخواهیم ولی به ربات نگفتیم، فقط گفتیم کنسرو
در مثال خودمون هم ما میدونیم گیم میخواهیم ولی رانتایم که هیچی نمیدونه هیچ، تایپ اسکریپ هم سرش کلاه گذاشتیم، اونم با تایپی به اسم any که کارش زدن توی سر ts هست.
+ حالا راه حل چیه؟
- ساده است، ما باید به رباتمون بگیم داخل بسته باید یک کنسرو تن ماهی باشه.
+ اینو چطوری مپ کنیم به کدمون؟
- باید parse کنیم
+ یعنی چی؟
- یعنی باید تک تک پراپرتی ها چک بشه.
یه همچین چیزی میشه:
خوب حالا آیا باید برای هر data structure ای که داشتیم اینارو دستی بنویسیم؟ نه
خوشبختانه کلی کتابخونه اون بیرون هست که خیلی راحت این کارو برای ما میکنن، معروف ترین هاش شامل io-ts ،zod ،runtypes و... هست
فعلا یه مثال برای پیاده سازی همین چیزی که نوشتیم با runtypes نشون میدم (البته نظر خودم روی io-ts هست)
حالا شاید بگید اینجوری داریم یه schema برای ts و یه schema برای رانتایم درست میکنیم، اکثر کتابخونه های decode کردن این قابلیت رو دارن که از روی schema خودشون تایپ رو هم بسازن تو این مثال اینجوری میشه:
همه چی عالی ولی اگه دقت کرده باشید توی عنوان این بند گفتم "یک تیر و دو نشان" و اگه یادتون باشه برای کد مثال گفتم ما با این کد دوتا مشکل داریم، مشکل اول همین بود، مشکل دوم ارور هندلینگ هست
یکی از مزیت های دیگه این کتابخونه ها و کلا parse کردن به ما میدن مجبور کردن ما به هندل کردن else case هست
اگه دقت کنید ما خروجی رو union کردیم چون ممکنه parse امون fail بشه و همین اضافه کردن parsing یه تیر و دو نشون زدیم
حتی توی مثال ربات امون هم اینجوریه، باید مشخص کنیم اگر تن ماهی نبود، ربات چی کار کنه.
یه نکته دیگه که شاید دقت کرده باشید اون price تایپش هم توی تایپ جنریت شده استرینگ شده هم توی تایپی که خودم نوشتم، این مشکلی هست که مربوط به ts میشه توی داکیومنت کتابخونه هم توضیح داده که چه مشکلی هست، ولی به طور کلی هیچ آسیبی توی رانتایم و parse کردن ما نمیزه، فقط مشکلمون اینه که تایپ درست جنریت نمیشه
این runtypes تابع های دیگه ای هم روی تایپاش داره مثل validate و check میتونید اونارو هم چک کنید