الكابوس الصامت لكل مطور React: event.target.value و TypeScript
هل سبق لك أن كتبت معالج onChange لحقل إدخال بسيط، وفجأة بدأ TypeScript يصرخ بالأحمر؟ مألوف: "Property 'value' does not exist on type 'EventTarget'". هذا السؤال على Stack Overflow حصد عشرات الآلاف من المشاهدات، وليس من دون سبب. يبدو الأمر بسيطًا - أخذ القيمة من حقل الإدخال؟ لكن الشيطان، كما هو الحال دائمًا، يكمن في التفاصيل.
في عام 2025، أصبحت بيئة TypeScript أكثر صرامة. الإصدارات الجديدة من المترجم (Compiler) تحلل الأنواع بشكل أفضل، وتوقفت "الحيل" القديمة عن العمل. إذا كنت تكتب في React أو تستخدم TypeScript ببساطة للعمل مع DOM، فهذه المقالة هي قارب النجاة الخاص بك. سنشرح ليس فقط كيفية كتابة أنواع (Typing) لـ onChange، ولكن أيضًا لماذا يعمل بهذه الطريقة وليس بأخرى. هيا بنا!
لماذا event.target.value مؤلم؟
دعنا نذهب مباشرة إلى صلب الموضوع. إليك مثال نموذجي يسبب خطأ:
const handleChange = (event: any) => { const value = event.target.value; // يعمل، لكن "any" شر};المشكلة هي أن event يمكن أن يكون أي شيء. TypeScript لا يعرف ما هو هذا العنصر وما إذا كان لديه خاصية value. علاوة على ذلك، استخدام any يقتل كل سحر الكتابة الثابتة (Static Typing). ستفقد الإكمال التلقائي والحماية من الأخطاء.
الطريق الصحيح هو استخدام أنواع محددة. لكن أي منها؟ دعنا نكتشف ذلك.
أفضل الممارسات لكتابة أنواع onChange في عام 2025
1. React: استخدام ChangeEvent
إذا كنت تعمل مع React، انسَ Event الأصلي. React يستخدم أحداثه الاصطناعية (Synthetic Events) - SyntheticEvent. لحقول الإدخال (inputs) و textarea و select، فإن ChangeEvent هو الخيار الأمثل.
import React, { ChangeEvent } from 'react';
const MyInput = () => { const handleChange = (event: ChangeEvent<HTMLInputElement>) => { const value = event.target.value; console.log(value); // value عبارة عن نص (string)، TypeScript سعيد };
return <input type="text" onChange={handleChange} />;};لماذا يعمل هذا؟ ChangeEvent<HTMLInputElement> هو نوع عام (Generic) يعرف بالتأكيد أن event.target هو HTMLInputElement. وهذا العنصر لديه خاصية value من النوع string. كل شيء منطقي وآمن من حيث الأنواع.
2. بالنسبة لـ textarea و select
نهج مماثل يعمل مع عناصر النماذج الأخرى. فقط قم بتغيير النوع العام (Generic):
ChangeEvent<HTMLTextAreaElement>- لـ<textarea>ChangeEvent<HTMLSelectElement>- لـ<select>ChangeEvent<HTMLInputElement>- لـ<input>
const handleTextareaChange = (event: ChangeEvent<HTMLTextAreaElement>) => { const text = event.target.value; // string};
const handleSelectChange = (event: ChangeEvent<HTMLSelectElement>) => { const selectedValue = event.target.value; // string};3. TypeScript الخالص (بدون React): Event + تأكيد النوع (type assertion)
إذا كنت لا تستخدم React، بل تعمل مباشرة مع DOM، فالأمر مختلف قليلاً. Event الأصلي لا يعرف خاصية value. ستحتاج إلى تحويل (cast) event.target إلى النوع الصحيح.
const inputElement = document.getElementById('my-input') as HTMLInputElement;
inputElement.addEventListener('change', (event: Event) => { const target = event.target as HTMLInputElement; const value = target.value; console.log(value);});هام: استخدم تحويل الأنواع (as HTMLInputElement) فقط عندما تكون متأكدًا بنسبة 100% من نوع العنصر. وإلا فإنك تخاطر بالحصول على خطأ في وقت التشغيل (runtime).