الصنف المجرد صنف أساسي يحمل abstract. لا يمكن إنشاؤه بـ new، ويمكنه تعريف توابع abstract بلا جسم، يجب على كل صنف فرعي تنفيذها.
يشارك Shape التابع describe() مع كل الأصناف الفرعية ويُلزم كلًا منها بتوفير area(). والنوع Shape[] يحمل أي مزيج من الأصناف الفرعية.
لا يمكن إنشاء نسخة من صنف مجرد
استدعاء new على صنف مجرد خطأ ترجمة، لأن الكائن سيحمل توابع بلا تنفيذ:
index.ts(6,11): error TS2511: Cannot create an instance of an abstract class.
هذه قاعدة وقت الترجمة. يُحذف abstract من الناتج، فيكون Shape المولَّد صنف JavaScript عاديًا، ويستطيع كود JavaScript بلا أنواع أن يستدعي new Shape(). حين تحتاج مكتبة إلى حماية وقت التشغيل، يمكن لدالة البناء أن تفحص new.target:
التوابع والخصائص المجردة
يجب على الصنف الفرعي تنفيذ كل عضو مجرد، أو أن يكون هو نفسه مجردًا. نسيان واحد هو الخطأ TS2515، ونصه مثل Non-abstract class 'Square' does not implement inherited abstract member area from class 'Shape'، أو TS2654 الذي يسردها كلها حين ينقص أكثر من عضو.
يمكن أن تكون الحقول والـ getters والتوابع كلها مجردة، ويمكن أن تكون protected:
التابع المجرد بلا جسم؛ وكتابة جسم له هي الخطأ TS1245 (Method 'x' cannot have an implementation because it is marked abstract). ولا يلزم أن يحتوي الصنف المجرد أي عضو مجرد إطلاقًا: abstract وحده يمنع إنشاء النسخ مباشرة.
نمط Template Method
هذا أشيع سبب لكتابة صنف مجرد: يحدد الصنف الأساسي ترتيب الخطوات، ويملأ كل صنف فرعي الخطوات التي تختلف.
يمكن أن تكون الأصناف المجردة عامة (generic)، كما هنا، فيختار كل صنف فرعي نوع السجل.
أنواع دوال البناء المجردة
النوع typeof Shape لصنف مجرد هو دالة بناء لا يُسمح لك باستدعائها، فالمعامل من النوع typeof Shape لا يستطيع تنفيذ new c(). حين يجب أن تقبل دالة أصنافًا مجردة (مثلًا لتسجيلها أو لفحص instanceof)، حدد نوعها بتوقيع إنشاء مجرد:
يقبل abstract new الأصناف المجردة والفعلية معًا؛ أما new العادي فلا يقبل إلا الأصناف الفعلية.
الصنف المجرد مقابل Interface
| الصنف المجرد | Interface | |
|---|---|---|
| يحتوي كودًا | نعم: توابع، وقيم ابتدائية للحقول، ودالة بناء | لا، أنواع فقط |
| موجود وقت التشغيل | نعم | لا، يُحذف |
instanceof | يعمل | غير ممكن |
| يمكن للصنف استخدام | واحد (extends) | عدة (implements) |
| معدّلات الوصول | private وprotected وpublic | أعضاء عامة فقط |
| نوع للكائنات العادية | فقط إن لم يكن فيه أعضاء private/protected، وعندها يفشل instanceof | نعم، أي قيمة لها الشكل نفسه |
القاعدة العملية: interface حين تحتاج فقط إلى وصف شكل (الحالة الأكثر شيوعًا، ويعمل مع كائنات الـ literal أيضًا)، وصنف مجرد حين تتشارك الأصناف الفرعية تنفيذًا حقيقيًا وتريد أن يُلزمها المترجم بملء الفراغات. ويجتمعان جيدًا: interface للعقد العام، وصنف مجرد كأساس مريح يطبّقه. راجع الـ interfaces مقابل الأنواع للمقارنة الشائعة الأخرى.
أخطاء شائعة
- استخدام صنف مجرد حيث يكفي interface. إن لم يكن في الصنف الأساسي كود، فهو interface بعبء إضافي وقت التشغيل وقيد الوراثة الواحدة.
- تجربة
static abstract. غير مسموح. الأعضاء الثابتة لا يمكن أن تكون مجردة. - توقّع خطأ وقت التشغيل من
newعلى صنف مجرد. لا يوجد خطأ ما لم تكتب فحصnew.target. - استدعاء تابع مجرد من دالة البناء الأساسية. يعمل قبل تهيئة حقول الصنف الفرعي، فيراها التنفيذ
undefined.
الأسئلة الشائعة
ما هو الصنف المجرد في TypeScript؟
صنف يحمل abstract لا يمكن إنشاؤه بـ new، ويمكنه تعريف أعضاء abstract دون تنفيذ. ويجب على الأصناف الفرعية تنفيذ كل عضو مجرد. إنه طريقة لمشاركة الكود وفرض شكل معين في آن واحد.
ما الفرق بين الصنف المجرد والـ interface في TypeScript؟
يمكن للصنف المجرد أن يحتوي كودًا حقيقيًا (توابع منفذة، وحقولًا بقيم ابتدائية، ودالة بناء) وهو موجود وقت التشغيل، فيعمل عليه instanceof؛ ولا يرث الصنف إلا من صنف واحد. أما الـ interface فلا كود فيه، ويُحذف وقت الترجمة، ويمكن للصنف تطبيق عدة interfaces. استخدم الصنف المجرد لمشاركة السلوك، والـ interface لوصف الشكل.
هل يمكن أن يكون للصنف المجرد دالة بناء في TypeScript؟
نعم. تعمل حين يستدعي صنف فرعي super(...). لكن لا يزال لا يمكنك استدعاء new على الصنف المجرد نفسه (TS2511: Cannot create an instance of an abstract class).
هل يمكن أن تكون التوابع الثابتة مجردة في TypeScript؟
لا. لا ينطبق abstract إلا على أعضاء النسخ؛ وstatic abstract هو الخطأ TS1243 ('static' modifier cannot be used with 'abstract' modifier). لفرض شكل ثابت، حدد نوع دالة البناء بـ interface يتضمن العضو الثابت وافحص الصنف مقابله.
هل يُفرض الصنف المجرد وقت التشغيل؟
لا. يُحذف abstract، فيكون الصنف المترجم JavaScript عاديًا ويستطيع كود JavaScript عادي أن يستدعي new عليه. إن كان ذلك مهمًا، افحص new.target في دالة البناء وارمِ خطأ.