عندما يكبر مشروع JavaScript، تصبح بعض الأخطاء واضحة فقط أثناء تشغيل الصفحة أو استقبال استجابة غير متوقعة من API. تضيف TypeScript فحصًا ساكنًا للأنواع قبل التشغيل، ما يساعد على اكتشاف حالات كثيرة مبكرًا ويوفر توثيقًا داخل الكود. لكنها لا تضمن صحة المنطق ولا تتحقق وحدها من البيانات التي تأتي من الشبكة وقت التشغيل.
ما ستخرج به من المقال
- استخدام الأنواع لوصف العقود.
- التعامل مع null وبيانات API بطريقة صريحة.
- التحقق وقت التشغيل من البيانات الخارجية.
ما الذي تضيفه TypeScript؟
تضيف TypeScript نظام أنواع يُفحص قبل التشغيل ثم تُحوّل الشيفرة إلى JavaScript. يستطيع المحرر اكتشاف تمرير سلسلة بدل رقم أو الوصول إلى خاصية غير معروفة. النوع عقد يوضح شكل البيانات والعمليات المسموحة. ابدأ بالأنواع الأساسية والكائنات والدوال واتحادات الأنواع، ثم تعلّم generics عندما تحتاجها؛ لا تجعل التعقيد هدفًا بحد ذاته.
اكتب أنواعًا تعبر عن حالات العمل
يمكن تمثيل حالة الطلب باتحاد مثل pending | paid | cancelled بدل سلسلة عشوائية، فتظهر الحالات التي يجب التعامل معها عند التعديل. تجنب any لأنه يعطّل الفحص، واستخدم unknown عند استقبال قيمة غير معروفة ثم تحقق منها. تذكر أن as SomeType لا يحوّل البيانات ولا يثبت صحتها؛ هو إخبار للمترجم أن يفترض نوعًا معينًا.
تحقق من البيانات وقت التشغيل
إذا أرسل الخادم JSON، فلا تصبح الاستجابة موثوقة لمجرد إضافة interface؛ فالأنواع تختفي من JavaScript الناتج. تحقق من الحقول المطلوبة والأنواع والقيم المسموح بها وقت التشغيل باستخدام schema validator أو فحص واضح. افصل الشكل القادم من الشبكة عن نموذج المجال إذا اختلفت قواعدهما، ثم حوّله إلى كائن داخلي موثوق نسبيًا بعد الفحص.
فعّل إعدادات صارمة وادمج الاختبارات
خيارات مثل strict وstrictNullChecks تجعل التعامل مع القيم المحتمل غيابها أوضح. في مشروع قديم، فعّلها تدريجيًا وعالج الأخطاء بدل إسكاتها بـ any. شغّل فحص الأنواع والاختبارات في CI، واستخدم المترجم أثناء refactoring لمعرفة الأماكن التي تحتاج تحديثًا. نجاح فحص الأنواع لا يثبت صحة سلوك البرنامج؛ اجمع بين الأنواع واختبارات تقيس النتائج.
اتحاد أنواع يوضّح حالات الطلب
type OrderStatus =
| "pending"
| "paid"
| "cancelled";
type Order = {
id: string;
status: OrderStatus;
total: number;
};
function canShip(order: Order): boolean {
return order.status === "paid";
}
النوع يوضح الحالات المسموحة
عندما تكتب OrderStatus اتحادًا محدودًا من القيم، يستطيع المحرر اكتشاف خطأ إملائي في الحالة أو فرع ناقص في المنطق قبل التشغيل. هذا مفيد خصوصًا عندما تختلف الإجراءات حسب حالة الطلب؛ فالطلب المدفوع يمكن شحنه، لكن الطلب الملغى لا ينبغي أن يمر في المسار نفسه.
مع ذلك، TypeScript لا تتحقق تلقائيًا من البيانات التي تصل من API أو نموذج أو ملف JSON عند وقت التشغيل. النوع يخبر المترجم بما يفترضه الكود؛ أما البيانات الخارجية فيجب التحقق منها فعليًا ثم تحويلها إلى شكل موثوق. هذا الفرق بين التحقق الساكن والتحقق وقت التشغيل من أهم قواعد استخدام TypeScript بأمان.
دراسة حالة مفصلة: من العرض إلى السبب الجذري
الحالة العملية هنا هي: تصل استجابة API بحقل غير متوقع رغم أن TypeScript يصفه على أنه رقم مضمون. لا يكفي أن نصلح العَرَض الظاهر؛ ينبغي رسم مسار البيانات أو الطلب من بدايته حتى النتيجة، ومعرفة أين نشأ الاختلاف. ابدأ بتسجيل ما حدث، وما المتوقع أن يحدث، والخطوة التي يمكن إثباتها بالقياس أو السجل أو الاختبار. بعد ذلك غيّر عاملًا واحدًا واضحًا، ثم أعد السيناريو نفسه للتحقق من أن السبب الجذري عولج. إذا تغيرت عدة مكونات دفعة واحدة، فسيصعب معرفة أيها أحدث الفرق.
اربط نتيجة الحالة بمبدأ المقال: ما الذي تضيفه TypeScript؟ واكتب أنواعًا تعبر عن حالات العمل. هذان المفهومان يحددان ما الذي يجب فحصه أولًا وما الذي يمكن تأجيله. سجّل أيضًا الحالات التي لم تختبرها، حتى لا يتحول نجاح سيناريو واحد إلى ادعاء بأن النظام كله خالٍ من المشكلة.
اختيار الأنواع دون الإفراط في التعقيد
استخدم unknown عندما تكون البيانات غير موثوقة، ثم ضيّق النوع عبر تحقق مفهوم بدل اللجوء سريعًا إلى any أو assertions. الأنواع العامة مفيدة عندما تصف علاقة حقيقية بين المدخل والنتيجة، لكنها تصبح عبئًا إذا صارت أصعب من الوظيفة نفسها. اجعل الأنواع تعكس نموذج المجال، لا تفاصيل تنفيذ يمكن تغييرها بسهولة.
تساعد الاتحادات المميّزة على تمثيل حالات متبادلة الاستبعاد مثل التحميل والنجاح والفشل، بدل مجموعة قيم Boolean يمكن أن تنتج تركيبات غير منطقية. عند تغيير واجهة، استخدم أخطاء المترجم لتحديد الأماكن التي يجب تحديثها، ثم اختبر السلوك الفعلي؛ نجاح بناء TypeScript لا يثبت صحة القواعد التجارية.
من المثال إلى التنفيذ في مشروع حقيقي
ابدأ من المثال البرمجي المعروض في المقال: «اتحاد أنواع يوضّح حالات الطلب». لا تنقله حرفيًا إلى الإنتاج قبل أن تحدد مدخلاته ومخرجاته وافتراضاته. اسأل ما الذي يحدث عند غياب قيمة، أو وصول بيانات غير متوقعة، أو تكرار الطلب، أو تأخر الاعتماد الخارجي. بعد ذلك نفّذ نسخة صغيرة داخل فرع مستقل وأضف اختبارًا للحالة الأساسية وآخر لحالة فشل.
استخدم هذه الخطوات كبداية: ابدأ بالأجزاء الأكثر عرضة لأخطاء البيانات. ثم عرّف أنواعًا للعقود المشتركة. ثم تحقق من JSON والمدخلات وقت التشغيل.. اجعل كل خطوة قابلة للمراجعة، ولا تجمع إعدادات المشروع الحقيقية والأسرار داخل مثال تعليمي. إذا اختلفت بيئتك عن المثال، وثّق الاختلاف وعدّل التنفيذ بناءً على التوثيق الرسمي بدل نسخ الحل دون فهم.
فهم أخطاء الأنواع ومعالجة البيانات الخارجية
إذا أصبحت دالة مليئة بـ as و! لإسكات المترجم، فهذه إشارة إلى أن تعريف البيانات أو الحدود بين الحالات غير واضح. ابدأ من المصدر: هل نوع الاستجابة دقيق؟ هل يمكن للقيمة أن تكون null؟ هل توجد حالة لم تُغطّ؟ أصلح النموذج بدل نشر تحويلات نوعية تفرض على المترجم قبول افتراضات لا يعرف صحتها.
عند استقبال JSON، تحقق من الحقول المطلوبة ونوعها ومجالاتها في وقت التشغيل، سواء بمكتبة schema أو بتحقق يدوي مناسب. بعد التحقق، مرّر النوع الناتج إلى بقية التطبيق. هذا يجعل حدود النظام واضحة ويمنع أن تتسلل قيمة خاطئة إلى منطق يعتمد على ضمانات غير حقيقية.
أسلوب تشخيص منهجي عند الفشل
عندما لا يعمل الحل، لا تبدأ بإضافة شرط آخر قبل تحديد دليل الفشل. دوّن المدخل، والنتيجة المتوقعة، والنتيجة الفعلية، والرسالة أو القياس الذي يدعم الاستنتاج. من الأخطاء المتكررة في هذا الموضوع: الاعتقاد أن interface يتحقق من JSON وقت التشغيل.. تعامل مع هذه النقطة كفرضية للفحص، لا كتشخيص مسبق؛ فقد يكون سبب المشكلة في طبقة مجاورة أو في افتراض غير صحيح بالاختبار.
قسّم النظام إلى حدود واضحة: مدخلات، ومعالجة، واعتماد خارجي، ونتيجة. اختبر كل حد على حدة قدر الإمكان، ثم نفّذ سيناريو متكاملًا. إذا أصلحت المشكلة، أضف اختبارًا يمنع رجوعها أو ملاحظة تشغيلية تساعد على كشفها مبكرًا. الهدف أن يصبح التشخيص قابلًا للتكرار من عضو آخر بالفريق، لا أن يظل مرتبطًا بذاكرة الشخص الذي اكتشف العطل.
خطة إدخال TypeScript إلى مشروع قائم
إذا كان المشروع مكتوبًا بجافاسكربت، فلا يلزم تحويل كل الملفات دفعة واحدة. ابدأ بوحدات مستقلة أو أكثر الأجزاء عرضة لأخطاء الأنواع، وأضف إعدادًا تدريجيًا يوضح الملفات الداخلة ضمن التحقق. لا تخفِ أخطاء كثيرة باستخدام any لمجرد إنهاء الانتقال سريعًا، لأن ذلك يقلل القيمة المتوقعة من التحويل.
فعّل الخيارات الصارمة تدريجيًا حسب حجم المشروع، واكتب أنواعًا لحدود API والنماذج الأساسية، وضع قواعد واضحة لتعامل الفريق مع الاستثناءات. راجع إصدارات الأدوات وطريقة البناء في CI، حتى يستخدم المطورون والمستودع نفس قواعد التحقق.
اعتبارات متقدمة قبل اعتماد الحل
في المشروع الحقيقي، لا يُقيّم الحل على نجاح الحالة السعيدة فقط. راجع السلوك عند الحمل، وحالات الفشل، والتغييرات المستقبلية، والحدود الأمنية ذات الصلة. من المهم هنا أن لا تعتبر النوع الساكن تحققًا من JSON؛ افحص البيانات عند حدود النظام وقلل التحويلات القسرية غير المبررة. لا تُعامل هذه النقاط كملحق لاحق؛ فقرار التصميم قد يصعب تغييره عندما تتراكم البيانات أو يعتمد عليه عملاء متعددون.
حدّد مقاييس أو اختبارات قبول قبل الدمج، ودوّن حدود الحل بوضوح. قد يكون الحل مناسبًا لموقع صغير لكنه يحتاج طابورًا أو cache أو بنية أكثر صرامة عند نمو الاستخدام. لا تضف تعقيدًا مسبقًا بلا دليل، لكن لا تتجاهل علامة واضحة على أن المتطلبات تتجاوز النسخة البسيطة. القرار الجيد يوازن البساطة الحالية مع كلفة التغيير المتوقعة ويحافظ على وضوح النظام للفريق.
كيف تعرف أن الأنواع حسّنت المشروع؟
راقب مقدار التحقق اليدوي المتكرر الذي اختفى، وعدد الحالات غير الممكنة التي أصبح المترجم يكشفها، وسهولة تعديل النموذج عبر الملفات. ليس الهدف إنتاج أكبر عدد من الأنواع، بل تقليل الافتراضات المخفية وتحسين قابلية القراءة والصيانة.
راجع نوعًا معقدًا مع مطور آخر: إذا لم يستطع فهمه دون تتبع عدة طبقات من generics، فربما تحتاج إلى تقسيمه إلى تعريفات أبسط. اختبر السلوك الأساسي لأن النوع الساكن لا يغطي أخطاء التوقيت أو الشبكة أو قواعد العمل غير الممثلة فيه.
تطبيق عملي ومعايير قبول
جرّب هذا التمرين على مشروع تجريبي أو فرع منفصل: تحقق من JSON غير صالح وقت التشغيل، ثم ضيّق النوع قبل استخدام القيمة. اكتب النتيجة التي تتوقعها قبل التنفيذ، وسجّل ما حدث بالفعل، ثم أصلح الفرق إن ظهر. جرّب كذلك حالة حدودية أو فشلًا مقصودًا بصورة آمنة. لا تستخدم بيانات حقيقية أو نظامًا إنتاجيًا لإحداث فشل تجريبي.
اعتبر المهمة مكتملة عندما تكون خطوات إعادة التجربة موثقة، والاختبار يثبت السلوك الأساسي وحالة الفشل، ولا توجد أسرار أو معلومات حساسة في السجل، ويستطيع مطور آخر فهم سبب الحل. إذا لم يتحقق هذا، فالمطلوب ليس إضافة مزيد من الكود مباشرة؛ أعد صياغة المشكلة ووضح الافتراض الذي لم يكن صحيحًا. هذه الطريقة تجعل المقال نقطة انطلاق لممارسة تقنية، لا مجرد قراءة نظرية.
أسئلة شائعة عن TypeScript
هل TypeScript تمنع كل أخطاء JavaScript؟ لا؛ تساعد على كشف فئة من الأخطاء الساكنة لكنها لا تستبدل الاختبارات والتحقق وقت التشغيل.
هل any ممنوع دائمًا؟ ليس بالضرورة، لكنه ينبغي أن يكون استثناءً محدودًا ومبررًا.
هل أحتاج مكتبة تحقق؟ عندما تأتي البيانات من حدود خارجية، تحتاج تحققًا وقت التشغيل، ويمكن أن تسهّل مكتبة schema ذلك.
خطة تطبيق عملية
- ابدأ بالأجزاء الأكثر عرضة لأخطاء البيانات.
- عرّف أنواعًا للعقود المشتركة.
- تحقق من JSON والمدخلات وقت التشغيل.
- فعّل فحص الأنواع في CI.
- عالج التحذيرات دون إخفائها عشوائيًا.
أخطاء شائعة انتبه لها
- الاعتقاد أن interface يتحقق من JSON وقت التشغيل.
- استخدام
asلتجاوز كل خطأ. - كتابة أنواع معقدة لا تضيف وضوحًا.
الخلاصة
تجعل TypeScript الافتراضات مرئية وتساعد على اكتشاف أخطاء التوافق مبكرًا. استخدمها لوصف العقود والحالات، وتحقق من البيانات الخارجية وقت التشغيل، وابقِ الاختبارات مسؤولة عن السلوك الفعلي.