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

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

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

ابدأ الدورة

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

البرمجة الوظيفية (FP) هي نموذج يعمل بنشاط على غزو عالم JavaScript و TypeScript في السنوات الأخيرة. للمطورين الملمين بالفعل بالأساسيات (الدوال النقية، ثبات البيانات)، تفتح طبقة جديدة من الإمكانيات: العمل مع monads و functors و applicatives والتركيبات المعقدة. في هذه المقالة، سنشرح مفاهيم FP المتقدمة في TypeScript مع أمثلة عملية.



1. ثبات البيانات والمشاركة الهيكلية

ثبات البيانات هو حجر الزاوية في FP. في TypeScript، يمكننا ضمان الثبات على مستوى النوع باستخدام readonly و ReadonlyArray. ومع ذلك، للعمل بكفاءة مع هياكل البيانات الكبيرة (مثل مخزن Redux)، يتم استخدام المشاركة الهيكلية — مكتبات مثل Immer أو Immutable.js.

لننظر إلى مثال باستخدام readonly ودالة لتحديث كائن متداخل بدون تغيير:

type User = {  readonly id: number;  readonly name: string;  readonly address: {    readonly city: string;    readonly zip: string;  };};

const updateUserCity = (user: User, newCity: string): User => ({ ...user, address: { ...user.address, city: newCity, },});

const user: User = { id: 1, name: 'Alice', address: { city: 'NYC', zip: '10001' } };const updatedUser = updateUserCity(user, 'LA');console.log(user.address.city); // NYC (الأصلي لم يتغير)console.log(updatedUser.address.city); // LA

ملاحظة: نقوم بإنشاء كائن جديد ولكننا نعيد استخدام الأجزاء غير المتغيرة (مثل id و name). هذه مشاركة هيكلية يدوية. للتداخل العميق، من الأفضل استخدام المكتبات.



2. Functors وتطبيقها

Functors هي حاوية تطبق طريقة map، مما يسمح بتطبيق دالة على محتويات الحاوية دون كسر هيكلها. في TypeScript، Array هو functor نموذجي. لكن يمكننا إنشاء functor خاص بنا، على سبيل المثال Maybe، لتجنب الأخطاء مع null و undefined.

لنقم بتنفيذ functor Maybe بسيط:

class Maybe<T> {  private constructor(private value: T | null) {}

static of<U>(val: U | null | undefined): Maybe<U> { return new Maybe(val ?? null); }

map<U>(fn: (val: T) => U): Maybe<U> { if (this.value === null) { return Maybe.of<U>(null); } return Maybe.of(fn(this.value)); }

getOrElse(defaultValue: T): T { return this.value !== null ? this.value : defaultValue; }}

// الاستخدامconst result = Maybe.of('hello') .map(s => s.toUpperCase()) .map(s => s + ' world') .getOrElse('fallback');console.log(result); // "HELLO WORLD"

const empty = Maybe.of<string>(null) .map(s => s.toUpperCase()) .getOrElse('default');console.log(empty); // "default"

يسمح functor Maybe بربط آمن لاستدعاءات الدوال دون القلق بشأن null. كل map يعيد Maybe جديدًا، محافظًا على السياق.



3. Monads: سلاسل العمليات ذات التأثيرات الجانبية

Monad هو functor يطبق أيضًا طريقة flatMap (غالبًا ما تسمى bind أو chain). يسمح "بفك تغليف" الحاويات المتداخلة. مثال كلاسيكي على Monad هو Promise أو Array. دعنا ننظر إلى Monad Either لمعالجة الأخطاء بدون استثناءات.

type Either<L, R> = Left<L> | Right<R>;

class Left<L> { readonly value: L; constructor(value: L) { this.value = value; } isLeft(): this is Left<L> { return true; } isRight(): this is Right<ne

المدونات

توصيات الكتب