«الخاصية 'value' غير موجودة في النوع 'EventTarget'» — كيف تتوقف عن الخوف وتبدأ في كتابة الأحداث في TypeScript
\n\nهل سبق لك أن كتبت معالج حدث في TypeScript، وحاولت الحصول على event.target.value — وفجأة حصلت على خط أحمر ورسالة: «الخاصية 'value' غير موجودة في النوع 'EventTarget'»؟ إذا كان الأمر كذلك، فأنت لست وحدك. على Stack Overflow، جمع هذا السؤال عشرات الآلاف من المشاهدات، وما زال يزعج المبتدئين والمطورين ذوي الخبرة في عام 2025.
المشكلة ليست في أن TypeScript «شرير» أو «معطل». بل العكس تمامًا: المحول البرمجي يحميك من الأخطاء الغبية التي قد تظهر في وقت التشغيل. دعنا نفهم لماذا يحدث هذا الخطأ وكيف نصلحه بشكل صحيح.
\n\nلماذا يشتكي TypeScript من event.target.value؟
\n\nالإجابة المختصرة: event.target له النوع EventTarget | null. و EventTarget ليس لديه خاصية value. لديه فقط ثلاث طرق: addEventListener، removeEventListener و dispatchEvent.
عندما تكتب:
\n\nconst handleChange = (event: React.ChangeEvent) => {\n const value = event.target.value; // ❌ خطأ!\n};\n\n\nيرى TypeScript: «مرحبًا، أنا لا أعرف أي عنصر HTML محدد تسبب في هذا الحدث. ربما هو <div>، الذي ليس لديه value، أو ربما <input> — لست متأكدًا. لذلك، أرفض تجميع هذا الكود».
هذه ميزة وليست خطأ. TypeScript يفرض عليك تحديد العنصر الذي تعمل معه بوضوح.
\n\nأفضل 3 طرق للحل (مناسبة لعام 2025)
\n\nهناك عدة طرق. سننظر في الأكثر شيوعًا وأمانًا.
\n\n1. التحويل باستخدام as HTMLInputElement (سريع ولكن بحذر)
\n\nالطريقة الأكثر شيوعًا هي تحويل event.target إلى النوع المطلوب:
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n const value = (event.target as HTMLInputElement).value;\n console.log(value);\n};\n\n\nالإيجابيات: بسيط، واضح، ويعمل.
\nالسلبيات: إذا أخطأت وكان الحدث في الواقع قادمًا من <textarea> أو <select>، فستحصل على undefined في وقت التشغيل. المحول البرمجي لن يحذرك.
من الأفضل استخدام هذه الطريقة عندما تكون متأكدًا تمامًا من نوع العنصر. على سبيل المثال، في النماذج البسيطة ذات الحقل الواحد.
\n\n2. استخدام event.currentTarget بدلاً من event.target
\n\nفي TypeScript، currentTarget مكتوب بشكل أكثر صرامة من target. إذا حددت معاملًا عامًا (generic parameter) للحدث، فسيكون currentTarget من النوع الصحيح:
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n const value = event.currentTarget.value; // ✅ يعمل بدون تحويل!\n};\n\n\nمهم: currentTarget هو العنصر الذي تم تسجيل معالج الحدث عليه. إذا قمت بالتسجيل على <input>، فسيكون currentTarget دائمًا هو هذا <input>. على عكس target، الذي قد يكون عنصرًا متداخلاً (مثل <span> داخل زر).
الإيجابيات: أكثر أمانًا من التحويل، ولا حاجة لكتابة تحويلات إضافية.
\nالسلبيات: غير مناسب لجميع السيناريوهات — على سبيل المثال، لأحداث الفقاعات (bubbling) قد لا يتطابق currentTarget مع العنصر المستهدف.
3. دالة عامة مع التحقق (الطريقة الأكثر أمانًا)
\n\nإذا كنت تريد أقصى درجات الأمان وإعادة الاستخدام، فاكتب دالة مساعدة تتحقق من نوع العنصر:
\n\nfunction getInputValue(event: Event): string