نوع never في TypeScript: كيفية إصلاح خطأ "not assignable to parameter"

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: لماذا يصرخ TypeScript على المصفوفة الفارغة؟

\n\n

هل سبق لك أن كتبت كودًا في TypeScript، وقمت بتشغيل التجميع، وحصلت على رسالة غامضة: "Argument of type 'X' is not assignable to parameter of type 'never'"؟ إذا كان الأمر كذلك، فأنت لست وحدك. هذا الخطأ هو حجر عثرة حقيقي للمطورين من جميع المستويات. في Stack Overflow، جمع هذا السؤال آلاف المشاهدات، واليوم سنقوم بتحليله مرة واحدة وإلى الأبد.

\n\n

تخيل الموقف: تعلن عن مصفوفة فارغة، وتحاول إضافة شيء إليها، لكن TypeScript يمنع العملية. هل هذا مألوف؟ دعنا نفهم لماذا يحدث هذا، والأهم من ذلك، كيف نصلحه.

\n\n

في هذه المقالة، لن نقوم فقط بترجمة الوثائق الجافة. سنسير في الطريق من ظهور الخطأ إلى حله الكامل مع أمثلة حقيقية يمكنك تطبيقها فورًا في مشاريعك.

\n\n

ما هو نوع never في TypeScript؟

\n\n

قبل أن نغوص في تفاصيل الأخطاء، دعنا نفهم النوع never نفسه. في TypeScript، يشير هذا النوع إلى "قيمة لا يمكن أن تكون موجودة". يبدو الأمر فلسفيًا، لكنه في الممارسة العملية بسيط:

\n\n
    \n
  • never هو نوع للدوال التي لا تنتهي أبدًا (حلقة لا نهائية) أو التي تطرح دائمًا استثناءً.
  • \n
  • never هو "قاع" نظام الأنواع. يمكنك تعيين never لأي شيء، لكن لا يمكن تعيين أي شيء لـ never (باستثناء never نفسه).
  • \n
\n\n

الآن تخيل أن TypeScript يرى مصفوفة فارغة ولا يعرف ما هي العناصر التي ستكون فيها. في وضع الكتابة الصارمة (strict: true في ملف tsconfig.json)، يقوم المترجم باستنتاج منطقي: "إذا كانت المصفوفة فارغة ولم يتم تحديد النوع، فهذا يعني أنه لا يمكن أن يكون فيها أي شيء — نوع العناصر هو never".

\n\n
// مثال: TypeScript يستنتج النوع على أنه never[]\nconst emptyArray = [];\nemptyArray.push("hello"); // خطأ!\n// Argument of type 'string' is not assignable to parameter of type 'never'\n
\n\n

ها هو ذا، ذلك الخطأ نفسه. TypeScript يحذر بأمانة: "لقد قلت إن المصفوفة يمكن أن تحتوي فقط على never، وتحاول إضافة سلسلة نصية. هذا غير مقبول!"

\n\n

السيناريوهات الرئيسية لظهور الخطأ

\n\n

1. مصفوفة فارغة بدون تعليق نوع

\n\n

الحالة الأكثر شيوعًا. يقوم المطور بإنشاء مصفوفة فارغة، ويخطط لملئها لاحقًا، لكنه ينسى تحديد النوع.

\n\n
// ❌ المشكلة\nconst items = []; // TypeScript يستنتج: any[] (إذا كان strict: false) أو never[] (إذا كان strict: true)\nitems.push({ id: 1, name: "Alice" }); // خطأ عند strict: true\n\n// ✅ الحل: حدد النوع صراحةً\nconst items: Array<{ id: number; name: string }> = [];\nitems.push({ id: 1, name: "Alice" }); // يعمل!\n
\n\n

2. دوال المصفوفات التي تُرجع never (filter, reduce)

\n\n

دالة filter مع شرط غير صحيح أو دالة reduce بدون قيمة ابتدائية يمكن أن تؤدي إلى استنتاج نوع never.

\n\n
// ❌ مشكلة مع filter\nconst numbers: (number | null)[] = [1, null, 2, null, 3];\nconst onlyNumbers = numbers.filter(item => item !== null); \n// قد يستنتج TypeScript النوع على أنه (number | null)[] أو حتى never[] إذا كان الشرط "معطوبًا"\n\n// ✅ الحل: استخدم حارس النوع (type guard)\nconst onlyNumbersSafe: number[] = numbers.filter((item): item is number => item !== null); \n\n// ❌ مشكلة مع reduce\nconst result = [1, 2, 3].reduce((acc, val) => acc + val); \n// خطأ! TypeScript لا يعرف النوع الابتدائي للمراكم\n\n// ✅ الحل: حدد القيمة الابتدائية\nconst resultSafe = [1, 2, 3].reduce((acc, val) => acc + val, 0); // يعمل!\n
\n\n

3. الأدوية العامة (Generics) والأنواع الشرطية

\n\n

حالة أكثر تعقيدًا — عندما لا يستطيع TypeScript استنتاج النوع بسبب التعميمات المعقدة.

\n\n
// ❌ المشكلة\nfunction getFirstElement<T>(arr: T[]): T | undefined {\n  return arr[0];\n}\n\nfunction processItems<T>                

المدونات

توصيات الكتب