Menu
flag Ar iconالعربيةdown icon

تحميل الدوال الزائد في TypeScript: Overload Signatures

يتيح التحميل الزائد للدوال في TypeScript أن تكون لدالة واحدة عدة توقيعات استدعاء، لكل منها نوع قيمة مُعادة خاص. تعرّف على نمط توقيعات التحميل مع التطبيق، والقواعد التي يفحصها المترجم، ومتى يكون معامل union أفضل، والتحميل الزائد في الأصناف.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

التحميل الزائد للدوال (function overloading) في TypeScript يعني كتابة عدة توقيعات استدعاء لدالة واحدة، يليها تطبيق واحد. يمكن لكل توقيع أن يقرن أنواع معاملات مختلفة بنوع قيمة مُعادة مختلف، فيحصل المستدعي على النوع الدقيق.

دون توقيعات التحميل كانت parse ستعيد number | number[] لكل استدعاء، وكان one + 1 سيكون خطأ حتى تضيّق الناتج بنفسك.

توقيعات التحميل والتطبيق

للدالة المحمّلة جزءان:

  1. توقيعات التحميل: تصريحات بلا جسم، واحد لكل شكل استدعاء مدعوم. وهي التوقيعات الوحيدة التي يستطيع المستدعون استخدامها.
  2. توقيع التطبيق: التصريح الأخير، مع الجسم. يجب أن تقبل معاملاته كل ما تقبله توقيعات التحميل، وأن يغطي نوع قيمته المُعادة نوع القيمة المُعادة لكل توقيع. وهو غير مرئي من الخارج.

الأنواع موجودة وقت الترجمة فقط، لذلك توجد دالة JavaScript واحدة وقت التشغيل. يجب أن يفحص التطبيق وسائطه (typeof وArray.isArray وarguments.length...) ليقرر ما يفعل. ويتحقق المترجم من توافق توقيعات التحميل مع التطبيق:

function format(value: string): string;
function format(value: number): number {
  return value;
}
// error TS2394: This overload signature is not compatible with its implementation signature.

الحل هو توسيع التطبيق: function format(value: string | number): string | number.

توقيع التطبيق غير قابل للاستدعاء

هذه القاعدة الأكثر مفاجأة للناس. يجب أن يطابق الاستدعاء أحد توقيعات التحميل وحده؛ ولا تجمع TypeScript بينها.

يطبع المترجم:

index.ts(12,19): error TS2769: No overload matches this call.
  The last overload gave the following error.
    Argument of type 'string | string[]' is not assignable to parameter of type 'string[]'.
      Type 'string' is not assignable to type 'string[]'.

يقبل التطبيق string | string[]، لكن المستدعين لا يرونه. أضف توقيع تحميل ثالثًا يأخذ الـ union ويعيد الـ union، فيُترجم الاستدعاء ويطبع [ 1, 2 ]:

function parse(input: string): number;
function parse(input: string[]): number[];
function parse(input: string | string[]): number | number[];
function parse(input: string | string[]): number | number[] {
  return Array.isArray(input) ? input.map(Number) : Number(input);
}

أعداد مختلفة من المعاملات

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

توقيع واحد بمعاملين اختياريين كان سيقبل makeDate(2024, 3) ويبني التاريخ الخاطئ بصمت. أما توقيعات التحميل فتحوّل ذلك إلى خطأ ترجمة (TS2575).

الترتيب مهم

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

function describe(value: unknown): string;   // matches everything
function describe(value: string): "text";    // never chosen
function describe(value: unknown): string {
  return typeof value === "string" ? "text" : "other";
}

const d = describe("hi"); // d: string, not "text"

بدّل موضعي التوقيعين الأولين فيصبح نوع describe("hi") هو "text".

التحميل الزائد أم معامل union؟

يستحق التحميل الزائد أسطره الإضافية عندما يعتمد نوع القيمة المُعادة على أنواع الوسائط. وعندما لا يعتمد، يكون توقيع واحد بمعامل union أقصر وأسهل قراءة، ويقبل وسائط union كان التحميل الزائد سيرفضها.

استخدمعندما
معامل unionيكون نوع القيمة المُعادة واحدًا لكل مدخل
معاملات اختياريةتختلف أشكال الاستدعاء فقط في وسائط أخيرة يمكن تركها بحرية
التحميل الزائديتغير نوع القيمة المُعادة مع الوسائط، أو يجب رفض بعض تركيبات الوسائط
نوع عاميُبنى نوع القيمة المُعادة من نوع الوسيط، مثل identity<T>(x: T): T

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

التحميل الزائد للدوال داخل الأصناف وللمُنشئات

تستخدم الدوال داخل الصنف النمط نفسه: توقيعات تحميل، ثم الدالة مع جسمها. ويمكن التحميل الزائد للمُنشئات بالطريقة نفسها.

يمكن للواجهات وأنواع الكائنات أيضًا إعلان توقيعات تحميل، كعدة call signatures أو عدة method signatures بالاسم نفسه. وكثير من الدوال المدمجة معلن بهذه الطريقة: مرّر المؤشر فوق reduce على مصفوفة في محرر فيعرض "+2 overloads".

الأسئلة الشائعة

هل تدعم TypeScript التحميل الزائد للدوال؟

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

ماذا تعني الرسالة "No overload matches this call"؟

الخطأ TS2769: الوسائط لا تناسب أيًا من توقيعات التحميل. لا يُحتسب توقيع التطبيق، لذلك يفشل استدعاء بوسيط union مثل string | string[] حتى عندما يقبله التطبيق. أضف توقيع تحميل يأخذ الـ union، أو استبدل توقيعات التحميل بتوقيع واحد.

متى أستخدم التحميل الزائد بدل نوع union؟

استخدم التحميل الزائد عندما يعتمد نوع القيمة المُعادة على أنواع الوسائط الممررة، مثلًا string يدخل فيخرج number لكن string[] يدخل فيخرج number[]. وعندما يكون نوع القيمة المُعادة واحدًا لكل المدخلات، يكون توقيع واحد بمعامل union أبسط ويقبل أيضًا وسائط union.

هل يمكن التحميل الزائد للدوال السهمية في TypeScript؟

ليس بصياغة تصريحات التحميل، التي لا تعمل إلا مع تصريحات function والدوال داخل الأصناف. يمكنك إعطاء متغير نوعًا محمّلًا بعدة call signatures، type Parse = { (s: string): number; (s: string[]): number[] }، لكن إسناد دالة سهمية إليه يحتاج عادة إلى تأكيد نوع، لذلك يكون تصريح function الخيار الأنظف.

لماذا توقيع التحميل لدي غير متوافق مع توقيع التطبيق؟

الخطأ TS2394 يعني أن أحد توقيعات التحميل يقبل أو يعيد شيئًا لا يقبله التطبيق. يجب أن تقبل معاملات التطبيق معاملات كل توقيع تحميل، وأن يكون نوع قيمته المُعادة متوافقًا مع نوع القيمة المُعادة لكل توقيع. توسيع التطبيق (غالبًا إلى union) يصلح ذلك.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن