النماذج الذهنية Mental Models
العدسات التي تحملها لكل ما تقابله
ليست ملخّصاً للمنهج، بل العدسات التي إن حملتها رأيت بها كل شيءٍ جديد تقابله. كلٌّ منها يختصر إقليماً في جملةٍ تُستدعى وقت الحاجة.
١. العدسة الأمّ: النوع مجموعة
النوع T مجموعةٌ من القيم. x: T تعني "x عضوٌ في T".
كل شيءٍ آخر نتيجة. حين تحتار في أي سلوك، ارسم دوائر فِن وطبّق ⊆. الإسناد احتواء، subtype هو subset، الاتحاد ∪، التقاطع ∩، التضييق طرح، never هو ∅، unknown هو المجموعة الكلّية. لا تحفظ القاعدة؛ اشتقّها من المجموعة.
٢. الجدار: عالمان لا يتلاقيان إلا حيث تبني جسراً
عالم الأنواع (وقت الترجمة، يُمحى) وعالم القيم (وقت التشغيل، يبقى). المُبرهِن يحكم الأول فقط.
كل خطأ إنتاجٍ حقيقي يسكن الجدار: بياناتٌ خارجية تدّعي نوعاً لم يُفحص. الجسور الثلاثة الوحيدة: type predicate، runtime validation، branded types. عامل كل ما يدخل من الخارج كـ unknown، وافحص عند الحدّ.
٣. الثنائية: اتحاد القيم = تقاطع القدرات
كلّما وسّعت مجموعة القيم (A | B)، ضيّقت ما تستطيع فعله (تقاطع العمليات).
مجموعةٌ أوسع ⟸ ضماناتٌ مشتركة أقلّ ⟸ قدراتٌ أقلّ. لتفعل شيئاً خاصاً بطرف، أثبته أوّلاً (تضييق). هذا يفسّر لماذا string | number لا يقبل .toUpperCase().
٤. النوع صفة الموضع لا المتغيّر
نفس المتغيّر له أنواعٌ مختلفة في أسطرٍ مختلفة، بحسب ما أثبته التدفّق قبله.
المترجم يبني رسم تدفّق ويعيد حساب المجموعة عند كل عقدة. return/throw المبكر يضيّق ما بعده. لهذا تتبّع الرمز لا القيمة (لغز y = x).
٥. الإنتاج يحفظ الاتجاه، الاستهلاك يقلبه (التباين)
ما تُنتجه الدالة covariant (يتبع ⊆)؛ ما تستهلكه contravariant (يقلب ⊆).
قاعدةٌ واحدة تحكم كل تباين: الدوال، المصفوفات، Promise، أي موضعٍ يظهر فيه معامل نوع. اسأل دائماً: هل هذا الموضع يُنتج أم يستهلك T؟
٦. TypeScript متساهلة عن قصد — والثقوب نقاط ثقة
لا نظام عمليّ يكون سليماً وكاملاً وقابلاً للحسم معاً (مشكلة التوقّف). TS اختارت التساهل.
any, as, !, تباين المصفوفات، الحدّ الخارجي — كلها تقول "ثق بي" بدل "أبرهن". ليست أخطاءً؛ موضعٌ مختار على طيفٍ حتميّ. عند كل as/! اسأل: "أي ادّعاءٍ أوقّع عليه بلا دليل؟".
٧. البنيوية = duck typing مبرهَن
العلاقة بين نوعين تُحسب من شكلهما لا من اسمهما. إن كان له الخصائص المطلوبة، فهو عضو.
نفس فلسفة Python ("إن مشى كبطّة")، لكن الفحص وقت الترجمة لا وقت الانفجار. حين تريد سلوكاً اسمياً (تمييز UserId عن PostId)، تصنعه بـ branded types.
٨. Generic = دالة على الأنواع؛ ونظام الأنواع لغةٌ كاملة
<T> معاملٌ يُمرَّر. conditional = if، infer = تفكيك، mapped = حلقة، recursion = تكرار.
حين تبرمج بالأنواع، تذكّر أنها تُحسب ثم تُمحى: تبرهن، لا تُنفّذ. والقوّة مُغرية للإفراط — استعملها حين تشتري سلامةً حقيقية.
كيف تستعمل هذه النماذج في درسك القادم
عند كل سطرٍ يربكك، اسأل بالترتيب:
- أي مجموعة؟ (نموذج ١) — ما القيم التي يسمح بها هذا النوع؟
- أي اتجاه احتواء؟ (نماذج ١، ٥) — من ⊆ من؟ يُنتج أم يستهلك؟
- أي موضعٍ في التدفّق؟ (نموذج ٤) — ماذا أُثبت عن هذا الرمز هنا؟
- هل عبرتُ الجدار؟ (نموذج ٢) — هل هذه البيانات مفحوصةٌ أم مُدّعاة؟
- هل هذه نقطة ثقة؟ (نموذج ٦) — هل كتبت
as/!/any؟ ما خطرها؟
خمسة أسئلة تحوّل "الحل الأعمى" إلى اشتقاقٍ واعٍ. هذا، من البداية، كان الهدف.