TypeScript: الدوال كمعاملات مع كتابة صارمة — تحليل كامل

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

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

ابدأ الدورة

TypeScript: الدوال كمعاملات مع كتابة صارمة — تحليل كامل مع أمثلة

\n\n

مرحباً يا زميل! إذا كنت قد كتبت يوماً ما بلغة JavaScript ومررت دالة إلى دالة أخرى (مثلاً في setTimeout أو Array.map)، فأنت بالفعل على دراية بمفهوم الدوال عالية المستوى. ولكن عندما يتعلق الأمر بـ TypeScript، يطرح سؤال منطقي: "هل يمكن كتابة الدالة التي أمررها كمعامل بشكل صارم؟". الإجابة — نعم، وهذه واحدة من أقوى ميزات اللغة.

\n\n

في هذه المقالة، سنشرح كيفية تعريف أنواع الدوال في TypeScript، وكيفية تمريرها كوسائط، وكيف لا نخطئ في التوقيعات. ستتعلم كيف تجعل الكود ليس فقط آمناً، بل أيضاً موثقاً ذاتياً. هيا بنا!

\n\n

بالمناسبة، هذا السؤال — من أكثر الأسئلة شيوعاً على Stack Overflow (آلاف المشاهدات). لذا إذا كنت تريد الفهم مرة واحدة وإلى الأبد — فأنت في المكان الصحيح.

\n\n

لماذا هذا مهم: الكتابة الصارمة توفر الوقت

\n\n

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

\n\n

إليك مثال لن يعمل بدون أنواع:

\n\n
// JavaScript (بدون أنواع) — الخطأ سيظهر فقط في وقت التشغيل\nfunction processData(callback) {\n    const result = callback(10, 20);\n    console.log(result);\n}\n\n// أوه، مررنا دالة بمعامل واحد\nprocessData((x) => x * 2); // NaN — لأن المعامل الثاني غير معرف
\n\n

مع TypeScript يمكننا تحديد التوقيع الذي نتوقعه من الاستدعاء بشكل صريح:

\n\n
// TypeScript — خطأ في مرحلة التجميع\nfunction processData(callback: (a: number, b: number) => number): void {\n    const result = callback(10, 20);\n    console.log(result);\n}\n\n// ✅ صحيح\nprocessData((x, y) => x + y); // 30\n\n// ❌ خطأ: النوع '(x: number) => number' لا يمكن تعيينه للنوع '(a: number, b: number) => number'\n// processData((x) => x * 2);
\n\n

رائع، أليس كذلك؟ الآن دعنا نستعرض جميع طرق تعريف هذه الأنواع.

\n\n

الطريقة 1: التوقيع المضمن (الأبسط)

\n\n

أبسط طريقة — تحديد توقيع الدالة مباشرة في المعاملات. هذا مناسب عندما تُستخدم الدالة-المعامل مرة واحدة فقط.

\n\n
// دالة تستقبل دالة أخرى برقمين وتعيد سلسلة نصية\nfunction formatNumbers(\n    a: number,\n    b: number,\n    formatter: (x: number, y: number) => string\n): string {\n    return formatter(a, b);\n}\n\n// الاستخدام\nconst result = formatNumbers(5, 10, (x, y) => `المجموع: ${x + y}`);\nconsole.log(result); // "المجموع: 15"
\n\n

المزايا: سريع، واضح.
\nالعيوب: إذا تكرر التوقيع، سيتعين عليك تكرار الكود.

\n\n

الطريقة 2: اسم مستعار للدالة (قابل لإعادة الاستخدام)

\n\n

إذا كنت تستخدم نفس التوقيع في عدة أماكن، فمن الأفضل استخراجه في اسم مستعار للنوع. هذا يجعل الكود أنظف وأسهل في الصيانة.

\n\n
// تعريف نوع الدالة\ntype Callback = (error: Error | null, data: string) => void;\n\n// دالة تستقبل Callback\nfunction fetchData(url: string, callback: Callback): void {\n    // محاكاة طلب غير متزامن\n    setTimeout(() => {\n        if (url) {\n            callback(null, "تم استلام البيانات");\n        } else {\n            callback(new Error("URL فارغ"), "");\n        }\n    }, 1000);\n}\n\n// الاستخدام\nfetchData("/api/data", (err, data) => {\n    if (err) {\n        console.error("خطأ:", err.message);\n    } else {\n        console.log("نجاح:", data);\n    }\n});
\n\n

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

المدونات

توصيات الكتب