البرمجة الوظيفية في TypeScript: المستوى المتقدم

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

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

ابدأ الدورة

البرمجة الوظيفية في TypeScript: المستوى المتقدم

\n\n

البرمجة الوظيفية (FP) هي نموذج ينظر إلى العمليات الحسابية على أنها تقييم للدوال الرياضية ويتجنب تغيير الحالة والبيانات القابلة للتغيير. TypeScript، كونه امتدادًا لـ JavaScript، يوفر نظام أنواع قوي يسمح بتطبيق العديد من مفاهيم FP على مستوى جديد من الأمان والتعبير. في هذه المقالة، سنستعرض التقنيات المتقدمة: من الثبات الصارم إلى العمل مع المونادات والفانكتورات.

\n\n

الثبات والعمل مع الهياكل غير القابلة للتغيير

\n\n

الثبات (عدم القابلية للتغيير) هو حجر الزاوية في FP. في TypeScript، يمكننا ضمان ذلك على مستوى الأنواع باستخدام معدّلات readonly والأنواع المساعدة.

\n\n
// عدم القابلية للتغيير العميق عبر ReadonlyDeep\ntype DeepReadonly<T> = {\n  readonly [P in keyof T]: DeepReadonly<T[P]>;\n};\n\ninterface User {\n  name: string;\n  address: { city: string; zip: number };\n}\n\nconst user: DeepReadonly<User> = {\n  name: "Alice",\n  address: { city: "Moscow", zip: 101000 }\n};\n\n// user.address.city = "SPb"; // خطأ! الخاصية للقراءة فقط
\n\n

لتحديث البيانات بأمان، استخدم الدوال النقية التي تُرجع كائنات جديدة:

\n\n
function updateZip(user: DeepReadonly<User>, newZip: number): DeepReadonly<User> {\n  return { ...user, address: { ...user.address, zip: newZip } };\n}\n\nconst updatedUser = updateZip(user, 190000);
\n\n

تركيب الدوال والكاري

\n\n

التركيب يسمح ببناء تحويلات معقدة من دوال بسيطة. في TypeScript، باستخدام الأدوية (Generics)، يمكننا إنشاء أدوات تركيب آمنة من حيث الأنواع.

\n\n
// نوع لتركيب دالتين\ntype Compose<A, B, C> = (f: (b: B) => C, g: (a: A) => B) => (a: A) => C;\n\nconst compose: Compose<any, any, any> = (f, g) => (x) => f(g(x));\n\n// مثال: تحويل سلسلة نصية إلى رقم وزيادته بمقدار 1\nconst toNumber = (s: string): number => parseInt(s, 10);\nconst increment = (n: number): number => n + 1;\n\nconst parseAndIncrement = compose(increment, toNumber);\nconsole.log(parseAndIncrement("5")); // 6
\n\n

الكاري يحول دالة تأخذ عدة وسائط إلى سلسلة من الدوال التي تأخذ وسيطة واحدة. هذا يجعل التركيب أكثر مرونة:

\n\n
// دالة كاري عامة مع الأنواع\nfunction curry<T1, T2, R>(fn: (a: T1, b: T2) => R): (a: T1) => (b: T2) => R {\n  return (a: T1) => (b: T2) => fn(a, b);\n}\n\nconst add = (a: number, b: number): number => a + b;\nconst curriedAdd = curry(add);\nconst add5 = curriedAdd(5);\nconsole.log(add5(3)); // 8
\n\n

الفانكتورات والمونادات: العمل مع السياقات

\n\n

الفانكتور هو هيكل بيانات يدعم عملية map. في TypeScript، يمكن أن يكون الفانكتور مصفوفة، أو Promise، أو نوعًا مخصصًا.

\n\n
// فانكتور Option (Maybe) للعمل الآمن مع null/undefined\ninterface Option<T> {\n  map<U>(fn: (value: T) => U): Option<U>;\n  getOrElse(defaultValue: T): T;\n}\n\nclass Some<T> implements Option<T> {\n  constructor(private value: T) {}\n  map<U>(fn: (value: T) => U): Option<U> {\n    return new Some(fn(this.value));\n  }\n  getOrElse(_: T): T {\n    return this.value;\n  }\n}\n\nclass None<T> implements Option<T> {\n  map<U>(_: (value: T) => U): Option<U> {\n    return new None<U>();\n  }\n  getOrElse(defaultValue: T): T {\n    return defaultValue;\n  }\n}\n\n// الاستخدام\nconst maybeNumber: Option<number> = new Some(42);\nconst result = maybeNumber.map(x => x * 2).getOrElse(0); // 84
\n\n

الموناد يوسع الفانكتور بطريقة flatMap (أو bind

المدونات

توصيات الكتب