«الخاصية value غير موجودة»: كيف توفق بين TypeScript وDOM
\n\nهل سبق لك أن كتبت معالج نموذج بسيط بلغة TypeScript، وقمت بتشغيل التجميع — وحصلت على سطر أحمر في وحدة التحكم: «The property 'value' does not exist on value of type 'HTMLElement'»؟ إذا كان الأمر كذلك — فأنت لست وحدك. هذا السؤال جمع آلاف المشاهدات على Stack Overflow، واليوم سنحلّه نهائياً.
\n\nيحدث الخطأ عندما تحاول الحصول على .value من عنصر يعتبره TypeScript مجرد HTMLElement. المشكلة أن HTMLElement هو نوع أساسي، والخاصية value موجودة فقط في HTMLInputElement و HTMLTextAreaElement والورثة المشابهين. TypeScript صارم: لن يسمح بالوصول إلى خاصية غير موجودة (من وجهة نظره).
لكن هذا ليس خطأ — بل ميزة. TypeScript يحميك من أخطاء وقت التشغيل. دعنا نفهم كيفية تحديد النوع بشكل صحيح وتحسين أداء الكود في نفس الوقت.
\n\nلماذا يعترض TypeScript؟
\n\nتخيل: أنت تكتب كوداً لحقل إدخال:
\n\nconst input = document.getElementById('myInput');\nconsole.log(input.value); // ❌ خطأ!\n\nالطريقة getElementById تُرجع النوع HTMLElement | null. HTMLElement لا يحتوي على الخاصية value. يوجد innerHTML و className و style — لكن ليس value. TypeScript يقول بصراحة: «لست متأكداً من أن هذا العنصر يحتوي على value».
بينما أنت متأكد أنه <input>. كيف تشرح ذلك للمترجم؟
الطريقة 1: تحويل الأنواع (Type Assertion)
\n\nالطريقة الأكثر شيوعاً وسرعة هي استخدام as:
const input = document.getElementById('myInput') as HTMLInputElement;\nconsole.log(input.value); // ✅ كل شيء يعمل\n\nالآن TypeScript يعلم: هذا HTMLInputElement، الذي يحتوي على value. بسيط وفعال. لكن هناك فارق بسيط: إذا لم يكن العنصر في الواقع <input> (مثلاً، هو <div>)، ستحصل على undefined في وقت التشغيل. TypeScript لن يساعدك — فهو يصدق تأكيداتك.
الطريقة 2: التحقق من النوع عبر instanceof
\n\nنهج أكثر أماناً — التحقق أثناء وقت التشغيل:
\n\nconst element = document.getElementById('myInput');\nif (element instanceof HTMLInputElement) {\n console.log(element.value); // ✅ TypeScript يضيق النوع تلقائياً\n} else {\n console.error('العنصر ليس input');\n}\n\nهذه الطريقة لا تحل مشكلة الأنواع فحسب، بل تجعل الكود أكثر موثوقية. أنت تتحقق صراحة من أن العنصر هو بالضبط ما تتوقعه. للأغراض التعليمية، أوصي بهذه الطريقة: فهي تعلمك التفكير في الأنواع.
\n\nالطريقة 3: الدوال العامة (Generics)
\n\nللمستخدمين المتقدمين — إنشاء غلاف باستخدام الأدوية:
\n\nfunction getInputElement<T extends HTMLElement>(id: string): T | null {\n const element = document.getElementById(id);\n if (element instanceof T) { // ❌ لا يمكن فعل ذلك، T هو نوع وليس قيمة\n return element;\n }\n return null;\n}\n\nللأسف، في TypeScript لا يمكن التحقق من instanceof مع الأدوية مباشرة. لكن يمكن استخدام التحميل الزائد للدوال أو المصانع. للمشاريع البسيطة، تكفي الطريقتان الأوليتان.
تحسين الأداء: لماذا النوع مهم
\n\nقد تسأل: «ما علاقة الأنواع بالسرعة؟» علاقة مباشرة. عندما يعرف TypeScript النوع الدقيق للعنصر، فإنه يولد كود JavaScript أكثر كفاءة. لننظر إلى مثال:
\n\n// سيء: TypeScript لا يعرف النوع\nfunction getValue(id: string): string {\n const el = document.getElementById(id);\n // في كل استدعاء — تحقق وتحويل\n return (el as HTMLInputElement).value || '';\n}\n\n// حس