كتابة أنواع دوال الاستدعاء (Callbacks) في TypeScript: كيفية تحديد نوع دالة المعالجة

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

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

ابدأ الدورة

كيف تتوقف عن استخدام any لدوال الاستدعاء وتبدأ حياة جديدة: دليل كامل لكتابة أنواع دوال المعالجة في TypeScript

\n\n

هل سبق لك أن كتبت شيئًا مثل callback: any في معاملات دالة؟ إذا كان الأمر كذلك — مرحبًا بك في النادي. إذا لم يكن كذلك — فأنت إما عبقري أو بدأت للتو رحلتك مع TypeScript. إنها مزحة. ولكن في كل مزحة نصيب من الحقيقة: وفقًا لبيانات Stack Overflow، فإن السؤال "How to define type for a function callback (as any function type, not universal any)" يجمع آلاف المشاهدات. وهذا ليس من قبيل الصدفة.

\n\n

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

\n\n

لماذا Function ليس أفضل من any؟

\n\n

أول رد فعل للمبتدئ هو استخدام النوع المدمج Function. باعتبار أنه نوع "أي دالة"، إذًا فهو مناسب. لكن لا. Function هو نوع "غير مفيد" تمامًا مثل any. فهو لا يعطي أي معلومات عن التوقيع: كم عدد المعاملات التي تقبلها الدالة، وماذا تُرجع، وما هي أنواع الوسائط. أنت فقط تقول للمترجم: "ثق بي، أنا أعرف ما أفعله". مفاجأة: عادةً لا تعرف.

\n\n
// سيء: لا يوجد تحقق من الأنواع\nfunction processData(callback: Function) {\n    callback(42, "hello"); // خطأ؟ ومن يدري\n}
\n\n

مع هذا النهج، تفقد كل مزايا الكتابة الثابتة للأنواع. دعنا نصلح الأمر.

\n\n

الخطوة 1. كتابة نوع أساسي لدالة الاستدعاء: بسيط وواضح

\n\n

أبسط وأصح طريقة هي تعريف نوع الدالة مباشرة في المعامل. لهذا، يُستخدم بناء الجملة بالسهم (معاملات) => نوع_الإرجاع. هذا هو function type في TypeScript.

\n\n
// جيد: تحديد التوقيع بوضوح\nfunction fetchData(url: string, onSuccess: (data: string) => void): void {\n    // ... بعض المنطق\n    const result = "بيانات من الخادم";\n    onSuccess(result);\n}\n\n// الاستخدام — مع الإكمال التلقائي والتحقق\nfetchData("/api/users", (data) => {\n    console.log(data.toUpperCase()); // data — string, كل شيء جيد\n});
\n\n

لاحظ void في نوع الإرجاع. هذا يعني أننا لا نتوقع قيمة محددة من دالة الاستدعاء. إذا أعادت دالة الاستدعاء شيئًا ما، فسيتم تجاهله. إذا كنت تتوقع نتيجة — استخدم نوعًا محددًا.

\n\n

الخطوة 2. دالة استدعاء بعدة معاملات

\n\n

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

\n\n
// نوع لدالة استدعاء مع خطأ ونتيجة\nfunction loadUser(\n    userId: number,\n    callback: (error: Error | null, user: { name: string; age: number } | null) => void\n): void {\n    // ... منطق التحميل\n    const user = { name: "آنا", age: 30 };\n    callback(null, user);\n}\n\n// الاستخدام\nloadUser(42, (err, user) => {\n    if (err) {\n        console.error("خطأ:", err.message);\n        return;\n    }\n    if (user) {\n        console.log(`مرحبًا، ${user.name}!`);\n    }\n});
\n\n

هل ترى كيف يقترح TypeScript تلقائيًا أنواع err و user؟ هذا هو كتابة أنواع دالة callback في العمل. بدون any، بدون سحر — فقط تحقق صارم.

\n\n

الخطوة 3. إنشاء أنواع قابلة لإعادة الاستخدام لدوال الاستدعاء

\n\n

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

\n\n
// تعريف                

المدونات

توصيات الكتب