Skip to content

شرح النوع `never` في تايب سكريبت

شرح النوع `never` في تايب سكريبت

16 نونبر 2022 | 00:00

يستخدم النوع never في لغة البرمجة TypeScript على نطاق ضيق جيدا مقارنة بالأنواع الأخرى التي توفرها هذه اللغة.

تستخدم لغة البرمجة TypeScript النوع never لتحديد الكائنات أو المتغيرات التي لا يمكنها أن تُعطى قيمة أبدا. وعلى وجه التحديد يُعطى النوع never إلى الدوال التي لا يمكنها إرجاع أي قيمة كتلك المحكوم عليها بالفشل الدائم عبر إطلاق خطأ (Throw error) وعدم وجود أي سيناريو لتلافي تلك الحالة.

الدوال الفاشلة

function alwaysFail(): never {
  throw new Error("Something went wrong!");
}

لا يجب هنا الخلط بين never و void ℹ️ لأن الدوال من النوع void في الحقيقة تقوم بإرجاع undefined وإن كان ذلك بشكل ضمني (Implicit).

الدالة التالية:

function voidFunc(): void {
  console.log("message");
}

هي مكافئة ومماثلة للدالة أسفله حيث نرجع undefined بشكل صريح (Explicit).

function voidFunc(): void {
  console.log("message");
  return undefined;
}

هناك حالة أخرى تعتبر فيها الدوال من النوع never، وهي عندما تضم بداخلها حلقة لانهائية (Infinite Loop). في هذه الحالة كذلك تعتبر الدالة غير ذات فائدة لأنها لن تقوم يوما بإرجاع شيء ولن يتم تنفيذها بنجاح مهما كان السيناريو.

function neverReachTheEnd(): never {
  while (true) {
    // ...
  }
}

كلتا الحالتين السابقتين تحدثان فقط كنتيجة لوجود خطأ في الكود، ولم أجد نفسي يوما أضطر لإنشاء دالة من نوع never لا قيمة لها.

ولكن هناك حالة ثالثة يستخدم فيها هذا النوع بشكل فعال ومفيد، حالة Conditional types أو الأنواع الشرطية.

الأنواع الشرطية

إذا كنت ملمّاً قليلا بلغة TypeScript فلا شك بأنك قابلت من قبل Exclude الذي يستخدم ك Utility Type ويمكننا من استبعاد أو إزالة عناصر محددة من نوع معين.

لاحظ المثال التالي:

type HttpMethods = "GET" | "POST" | "PUT" | "DELETE";
type MutationHttpMethods = Exclude<HttpMethods, "GET">; // 'POST' | 'PUT' | 'DELETE'

لدينا نوع اسمه HttpMethods يضم اتحاد لأربعة أنواع مختلفة وهي: GET - POST - PUT - DELETE. استخدمنا Exclude لإنشاء نوع ثاني اسمه MutationHttpMethods من أجل الحصول على HTTP Methods الخاصة بعمليات التعديل والتغيير على الموارد في الخادم (ومنه جاء الإسم Mutation) فقط.

المساعد Exclude سيقوم بهذا العمل كما يجب حيث سيستبعد GET من بين HTTP Methods الأخرى ويعطينا في النهاية 'POST' | 'PUT' | 'DELETE'، وهو ما نريده.

ولكن ما علاقة هذا بِ never ؟

للإجابة على هذا السؤال سنلقي نظرة على الشفرة المصدرية للنوع Exclude ولنرى كيف تم إنشاؤه 👇

node_modules/typescript/lib/lib.es5.d.ts
type Exclude<T, U> = T extends U ? never : T;

هذا الصنف من الأنواع يعرف في تايب سكريبت ب Conditional Type أو النوع الشرطي لأنه يأخذ قيمته بناء على تحقق شرط أو شروط محددة.

يقوم تايب سكريبت بالتأكد من عناصر النوع الأصلي T واحدا واحدا.

إذا كان بالإمكان إسناد النوع T إلى النوع U فالشرط يقوم بإرجاع never، وفي حالة العكس يرجع النوع T نفسه.

في الأخير سنحصل على اتحاد لعدة أنواع من بينها نوع واحد من never أو أكثر. وكما هو معلوم في TypeScript فإن اتحاد never مع أي نوع آخر X كيفما كان فسيعطينا X لأن never نوع لا قيمة له أمام الأنواع الأخرى (Bottom type). هو بمثابة الرقم صفر في عمليات الجمع.

أعرف أن هذا الكلام قد لا يعني لك شيئا خاصة إذا لم يكن لديك حد أدنى من الخبرة مع تايب سكريبت، ولذلك وضعت لك الصورة أسفله لعلها تكون أبلغ مني في إيصال الفكرة 😄

Exclude utility type in TypeScript

إذن بفضل النوع الأساسي never استطعنا بناء نوع آخر مركب مثل Exclude، وهناك أنواع مركبة (Utility Types) أخرى تستعين ب never خلف الكواليس. لذلك أنصحك بالدخول للشفرة المصدرية ل TypeScript ومحاولة قراءة تلك الأنواع واستكشافها. هناك الكثير من الأفكار ستساعدك في الإرتقاء بمستواك في هذه اللغة.

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