كيفية التحقق من نوع الفئة في TypeScript: من المبتدئ إلى المحترف
\n\nهل سبق لك أن كتبت كودًا بلغة TypeScript، واستقبلت كائنًا من نوع غير معروف وفكرت: "من أنت أيها الغريب؟" هذا السؤال يؤرق آلاف المطورين - وليس من قبيل الصدفة أن موضوع التحقق من نوع الفئة في TypeScript حصد مئات الآلاف من المشاهدات على Stack Overflow. واليوم سنفهم مرة وإلى الأبد كيفية التحقق من أنواع الفئات في TypeScript بشكل صحيح.
\n\nفي هذه المقالة سوف تتعلم:
\n- \n
- لماذا لا يعمل
typeofمع الفئات \n - كيفية استخدام
instanceofكأداة متعددة الاستخدامات \n - ما هي حراس النوع (type guards) وكيف تنقذ كودك \n
- كيفية كتابة التحقق الخاص بك من النوع باستخدام
is\n - أمثلة حقيقية من الممارسة \n
هيا بنا!
\n\nالمشكلة: لماذا typeof عاجز هنا؟
\n\nفي JavaScript يوجد عامل typeof يعمل بشكل ممتاز مع الأنواع البدائية:
typeof "hello" // "string"\ntypeof 42 // "number"\ntypeof true // "boolean"\n\nولكن عندما يتعلق الأمر بالفئات، يفشل typeof:
class Dog {}\nclass Cat {}\n\nconst pet = new Dog();\n\ntypeof pet; // "object" — غير مفيد!\ntypeof Dog; // "function" — ليس هذا أيضًا\n\nلماذا؟ لأن الفئات في JavaScript هي مجرد سكر نحوي فوق دوال البناء (constructor functions). typeof يرى فقط "object" أو "function"، لكنه لا يميز أي فئة بالضبط.
هنا يأتي دور التحقق من نوع الفئة في TypeScript - ترسانة كاملة من الأدوات التي ستساعدك على تحديد أي فئة تتعامل معها بدقة.
\n\nالطريقة 1: instanceof - مساعدك الرئيسي
\n\nالطريقة الأكثر مباشرة ووضوحًا للتحقق من نوع الفئة هي العامل instanceof. يتحقق مما إذا كانت الفئة موجودة في سلسلة النماذج الأولية (prototype chain) للكائن:
class Animal {\n name: string;\n constructor(name: string) {\n this.name = name;\n }\n}\n\nclass Dog extends Animal {\n bark() {\n console.log("Woof!");\n }\n}\n\nclass Cat extends Animal {\n meow() {\n console.log("Meow!");\n }\n}\n\nfunction handleAnimal(animal: Animal) {\n if (animal instanceof Dog) {\n // TypeScript يعلم أن animal هو Dog\n animal.bark();\n } else if (animal instanceof Cat) {\n animal.meow();\n } else {\n console.log(`Unknown animal: ${animal.name}`);\n }\n}\n\nconst rex = new Dog("Rex");\nhandleAnimal(rex); // "Woof!"\n\nلاحظ: بعد التحقق animal instanceof Dog، يقوم TypeScript تلقائيًا بتضييق النوع (type narrowing) ويسمح باستدعاء دوال Dog دون أخطاء. هذا هو سحر حراس النوع (type guards) - المدافعين المدمجين عن الأنواع.
متى لا يعمل instanceof؟
\n- \n
- عند العمل مع نوافذ/إطارات مختلفة (كائنات عامة مختلفة) \n
- إذا كانت الفئات محددة في وحدات مختلفة بإصدارات مختلفة \n
- عند التحقق من الواجهات (interfaces) (فهي غير موجودة في وقت التشغيل) \n
الطريقة 2: حراس النوع المخصصون مع العامل is
\n\nأحيانًا لا يكون instanceof كافيًا. على سبيل المثال، قد ترغب في التحقق ليس فقط من الفئة ولكن أيضًا من شروط إضافية. هنا يأتي دور حراس النوع المخصصين مع الكلمة المفتاحية is:
interface Bird {\n fly(): void;\n layEggs(): void;\n}\n\ninterface Fish {\n swim(): void;\n layEggs(): void;\n}\n\nfunction isFish(pet: Bird | Fish): pet is Fish {\n return (pet as Fish).swim !== undefined;\n}\n\nfunction move(pet: Bird | Fish) {\n if (isFish(pet)) {\n // TypeScript يعلم: pet هو Fish\n pet.swim();\n } else {\n pet.fly();\n }\n}\n\nلاحظ الصياغة: pet is Fish في نوع الإرجاع. هذا يخبر TypeScript: "إذا أعادت هذه الدالة true، فإن المعامل pet هو Fish". الآن سيقوم TypeScript بتضييق النوع أينما استخدمت