المقدمة: لماذا لا تعمل الواجهات في وقت التشغيل؟
\n\nمرحبًا، يا صديقي! إذا كنت قد كتبت كودًا بلغة TypeScript من قبل، فمن المحتمل أنك واجهت هذا الموقف: تقوم بإنشاء واجهة مثالية، وتصف جميع الحقول، وتتحقق من الأنواع في وقت الترجمة — وفجأة تدرك أن الكود الخاص بك في وقت التشغيل يتصرف كما لو أنه لا توجد واجهات. يبدو مألوفًا؟ 🎯
\n\nهذا أحد أكثر الأسئلة شيوعًا على Stack Overflow: "التحقق من نوع الواجهة باستخدام TypeScript" — آلاف المشاهدات، ومئات الإجابات. وليس من المستغرب، لأن TypeScript يمحو الواجهات أثناء الترجمة. على عكس الكلاسات، فإن الواجهات هي بناء تصريحي بحت للمطور والمترجم. إنها غير موجودة في JavaScript.
\n\nفي هذه المقالة، سنكتشف كيفية تجاوز هذا القيد، ونتعلم كيفية التحقق مما إذا كان الكائن يطابق واجهة في وقت التشغيل، ونجيب على الأسئلة الأكثر شيوعًا. هيا بنا! 🚀
\n\nلماذا نريد التحقق من الواجهات؟
\n\nتخيل: تستقبل بيانات من API، أو نموذج، أو WebSocket. أنت واثق من أنها تطابق واجهة User، ولكن فجأة يصل كائن بدون حقل email أو بنوع خاطئ. إذا لم تتحقق — سيتعطل الكود الخاص بك مع خطأ. واجهات TypeScript تتحقق فقط أثناء التطوير، وليس في الإنتاج. لذلك، فحوصات وقت التشغيل ضرورية.
المشكلة: الواجهات غير موجودة في وقت التشغيل
\n\nفي TypeScript، الواجهات هي "سكر نحوي" للكتابة الثابتة. عندما تقوم بترجمة الكود الخاص بك، تختفي الواجهات. إليك مثال بسيط:
\n\ninterface User {\n name: string;\n age: number;\n}\n\nconst data: unknown = JSON.parse('{"name": "Alice"}');\nif (data instanceof User) { // ❌ خطأ! User هي واجهة، وليست كلاس\n console.log(data.name);\n}\n\nسيقول المترجم: 'User' refers only to a type, but is being used as a value here. هذا هو المأزق الرئيسي للمبتدئين. لكن لا تقلق — هناك عدة طرق مثبتة لتجاوز هذا.
الطريقة 1: حراس النوع المعرّفون من قبل المستخدم
\n\nالطريقة الأكثر شيوعًا وأناقة هي كتابة دالة حارس نوع. تقوم بالتحقق من هيكل الكائن وإرجاع true/false، و TypeScript "تفهم" أنه داخل الكتلة التي تحتوي على true، فإن الكائن يطابق الواجهة بالتأكيد.
interface User {\n name: string;\n age: number;\n email?: string; // حقل اختياري\n}\n\nfunction isUser(obj: unknown): obj is User {\n if (typeof obj !== 'object' || obj === null) return false;\n\n const user = obj as Record<string, unknown>;\n return (\n typeof user.name === 'string' &&\n typeof user.age === 'number' &&\n (user.email === undefined || typeof user.email === 'string')\n );\n}\n\n// مثال على الاستخدام\nconst data: unknown = JSON.parse('{"name": "Bob", "age": 30}');\n\nif (isUser(data)) {\n // الآن TypeScript تعلم أن البيانات من نوع User\n console.log(data.name.toUpperCase()); // ✅ يعمل\n}\n\nالإيجابيات: تحكم كامل، يتم استنتاج الأنواع بشكل صحيح، سهل التوسيع.
\nالسلبيات: تحتاج إلى الكتابة يدويًا لكل واجهة (ولكن هناك مكتبات!)
ما هو obj is User؟
\n\nهذا هو بناء جملة حارس النوع. يخبر TypeScript: "إذا أعادت الدالة true، فيمكن اعتبار الوسيطة obj من نوع User". هذا هو السحر الذي يجعل التحقق في وقت التشغيل مفيدًا للتحليل الثابت.
الطريقة 2: استخدام الكلاسات بدلاً من الواجهات
\n\nالكلاسات هي بناء TypeScript الوحيد الذي يستمر في JavaScript. إذا كنت بحاجة إلى التحقق من النوع في وقت التشغيل، يمكنك استبدال الواجهة بكلاس والتحقق باستخدام instanceof.
class User {\n constructor(\n public name: string,\n public age: number,\n public email?: string\n ) {}\n}\n\nconst data = new User('Charlie', 25);\nif (data instanceof User) {\n consol