كيف تتوقف عن استخدام any لدوال الاستدعاء وتبدأ حياة جديدة: دليل كامل لكتابة أنواع دوال المعالجة في TypeScript
\n\nهل سبق لك أن كتبت شيئًا مثل callback: any في معاملات دالة؟ إذا كان الأمر كذلك — مرحبًا بك في النادي. إذا لم يكن كذلك — فأنت إما عبقري أو بدأت للتو رحلتك مع TypeScript. إنها مزحة. ولكن في كل مزحة نصيب من الحقيقة: وفقًا لبيانات Stack Overflow، فإن السؤال "How to define type for a function callback (as any function type, not universal any)" يجمع آلاف المشاهدات. وهذا ليس من قبيل الصدفة.
المشكلة هي أن any يشبه الثقب الأسود: فهو يمتص كل أمان الأنواع في الكود الخاص بك. تفقد الإكمال التلقائي، والتحقق من الوسائط، والقدرة على اكتشاف الأخطاء في مرحلة الترجمة. ولكن الأمر الأكثر إزعاجًا هو أن any كنوع لدالة الاستدعاء يقتل فكرة TypeScript نفسها. دعنا نفهم كيفية كتابة نوع دالة callback بشكل صحيح في معامل دالة، لجعل الكود آمنًا وأنيقًا في نفس الوقت.
لماذا Function ليس أفضل من any؟
\n\nأول رد فعل للمبتدئ هو استخدام النوع المدمج Function. باعتبار أنه نوع "أي دالة"، إذًا فهو مناسب. لكن لا. Function هو نوع "غير مفيد" تمامًا مثل any. فهو لا يعطي أي معلومات عن التوقيع: كم عدد المعاملات التي تقبلها الدالة، وماذا تُرجع، وما هي أنواع الوسائط. أنت فقط تقول للمترجم: "ثق بي، أنا أعرف ما أفعله". مفاجأة: عادةً لا تعرف.
// سيء: لا يوجد تحقق من الأنواع\nfunction processData(callback: Function) {\n callback(42, "hello"); // خطأ؟ ومن يدري\n}\n\nمع هذا النهج، تفقد كل مزايا الكتابة الثابتة للأنواع. دعنا نصلح الأمر.
\n\nالخطوة 1. كتابة نوع أساسي لدالة الاستدعاء: بسيط وواضح
\n\nأبسط وأصح طريقة هي تعريف نوع الدالة مباشرة في المعامل. لهذا، يُستخدم بناء الجملة بالسهم (معاملات) => نوع_الإرجاع. هذا هو function type في TypeScript.
// جيد: تحديد التوقيع بوضوح\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 في نوع الإرجاع. هذا يعني أننا لا نتوقع قيمة محددة من دالة الاستدعاء. إذا أعادت دالة الاستدعاء شيئًا ما، فسيتم تجاهله. إذا كنت تتوقع نتيجة — استخدم نوعًا محددًا.
الخطوة 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، بدون سحر — فقط تحقق صارم.
الخطوة 3. إنشاء أنواع قابلة لإعادة الاستخدام لدوال الاستدعاء
\n\nإذا تم استخدام نفس نوع دالة الاستدعاء في عدة أماكن، فلا داعي لتكراره في كل مرة. قم باستخراجه إلى type alias منفصل أو interface. هذا يحسن قابلية القراءة ويبسط الصيانة.
\n\n// تعريف