يستخدم النوع 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
ولنرى كيف تم إنشاؤه 👇
type Exclude<T, U> = T extends U ? never : T;
هذا الصنف من الأنواع يعرف في تايب سكريبت ب Conditional Type أو النوع الشرطي لأنه يأخذ قيمته بناء على تحقق شرط أو شروط محددة.
يقوم تايب سكريبت بالتأكد من عناصر النوع الأصلي T
واحدا واحدا.
إذا كان بالإمكان إسناد النوع T
إلى النوع U
فالشرط يقوم بإرجاع never، وفي حالة العكس يرجع النوع T نفسه.
في الأخير سنحصل على اتحاد لعدة أنواع من بينها نوع واحد من never
أو أكثر. وكما هو معلوم في TypeScript فإن اتحاد never
مع أي نوع آخر X
كيفما كان فسيعطينا X
لأن never
نوع لا قيمة له أمام الأنواع الأخرى (Bottom type). هو بمثابة الرقم صفر في عمليات الجمع.
أعرف أن هذا الكلام قد لا يعني لك شيئا خاصة إذا لم يكن لديك حد أدنى من الخبرة مع تايب سكريبت، ولذلك وضعت لك الصورة أسفله لعلها تكون أبلغ مني في إيصال الفكرة 😄
إذن بفضل النوع الأساسي never
استطعنا بناء نوع آخر مركب مثل Exclude
، وهناك أنواع مركبة (Utility Types) أخرى تستعين ب never
خلف الكواليس. لذلك أنصحك بالدخول للشفرة المصدرية ل TypeScript ومحاولة قراءة تلك الأنواع واستكشافها. هناك الكثير من الأفكار ستساعدك في الإرتقاء بمستواك في هذه اللغة.