مقدمة: مكونك "صامت" لأنه لا يعرف كيف يُولد
أنت تكتب بلغة TypeScript مع React، كل شيء يسير بسلاسة. وفجأة! المُصرّف (compiler) يرمي في وجهك رسالة غامضة:
JSX element type 'MyComponent' does not have any construct or call signatures.صورة مألوفة؟ إذا كنت مبتدئًا، قد يبدو هذا الخطأ وكأنه تعويذة من عالم موازٍ. لكن لا تتعجل في الذعر. في الحقيقة، الأمر بسيط: TypeScript يخبرك أن ما تحاول استخدامه كمكون JSX، ليس في الحقيقة دالة ولا كلاس يمكن "استدعاؤه" أو "بناؤه".
في هذه المقالة، سنشرح هذا الخطأ بطريقة مبسطة: لماذا يحدث، وكيفية إصلاحه، وماذا تعني "construct or call signatures". هيا بنا — سنفهم الأمر بمرح وأمثلة!
ماذا تعني "construct or call signatures"؟
لنتخيل أن مكون JSX هو مطعم. تدخل، تجلس على طاولة (تمرر props) وتنتظر حتى يُحضر لك الطعام (return JSX). لكن إذا كان المطعم مغلقًا للصيانة أو غير موجود أساسًا، فلن تتمكن من تقديم طلب.
في TypeScript، "construct signature" هي قدرة الكلاس على أن يُستدعى باستخدام new (كمُنشئ). "Call signature" هي قدرة الدالة على أن تُستدعى مباشرة. مكونات JSX في React يمكن أن تكون إما دوالاً أو كلاسات. إذا لم يرَ TypeScript في "مكونك" أيًا من هذين الأمرين — فإنه يُصدر خطأ.
حالة نموذجية:
// غير صحيحconst MyComponent = someObject;// <MyComponent /> — خطأ!
// صحيحconst MyComponent: React.FC<Props> = (props) => { return <div>مرحبًا</div>;};// <MyComponent /> — يعمليحدث الخطأ عندما تحاول استخدام شيء في JSX ليس دالة أو كلاس. على سبيل المثال: نص، رقم، كائن، أو متغير غير مُهيأ.
أهم 5 أسباب للخطأ (مع أمثلة أكواد بلغة TypeScript)
سنشرح السيناريوهات الأكثر شيوعًا التي قد تقع فيها في هذا الفخ. لكل حالة، سأعرض كيف تبدو المشكلة وكيفية إصلاحها.
1. المكون هو كائن وليس دالة
أكثر فخ شائع. تستورد شيئًا معتقدًا أنه مكون، لكنه في الحقيقة كائن عادي يحتوي على بيانات أو إعدادات.
// ❌ خطأimport userData from './userData';// userData = { name: 'آنا', age: 25 }function App() { return <userData />; // userData — كائن، ليس مكونًا!}
// ✅ الإصلاحimport UserCard from './UserCard';function App() { return <UserCard user={userData} />;}كيف تتحقق: مرر مؤشر الفأرة فوق المتغير في بيئة التطوير المتكاملة (IDE). إذا أظهر TypeScript نوعًا مثل object أو Record<string, any> أو شيئًا مشابهًا بدون () => JSX.Element — فهذا ليس مكونًا.
2. المكون هو نص أو رقم
أحيانًا بسبب عدم الانتباه، قد تمرر إلى JSX قيمة حرفية أو متغيرًا من نوع بدائي.
// ❌ خطأconst greeting = "مرحبًا بالعالم!";function App() { return <greeting />; // greeting من نوع string}
// ✅ الإصلاحfunction App() { return <h1>{greeting}</h1>; // استخدمه كنص، وليس كمكون}3. المكون مستورد بشكل غير صحيح
إذا كنت تستخدم export default وتستورد بدون أقواس معقوفة، لكن التصدير كان مُسمى — ستحصل على undefined. و undefined في JSX هو نفس الخطأ.
// في ملف Button.tsxexport const Button = () => <button>اضغط</button>;// export default غير موجود!
// ❌ خطأimport Button from './Button'; // Button = undefinedfunction App() { return <Button />;}
// ✅ الإصلاحimport { Button } from './Button'; // استيراد مُسمى// أو أضف export default في ملف Button.tsx