0Pricing
TypeScript Academy · درس

مشكلة HKT في TypeScript

افهموا سبب صعوبة مُنشئات الأنواع العامة

مشكلة HKT في TypeScript درس مجاني في TypeScript Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في TypeScript Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة TypeScript Academy 4 دروس في المجموع.

ما النوع عالي الرتبة

يجرّد النوع عالي الرتبة (HKT) من مُنشئ نوع، لا من نوع محدد، مثل Array أو Promise، وهو شيء يحتاج إلى وسيط كي يصبح نوعًا فعليًا.

إن Array وحده ليس نوعًا، أما Array<string> فهو نوع. إن Array دالة تعمل على الأنواع، وتتيح لنا HKTs التعامل مع هذه الدوال بصورة عامة.

type StringArray = Array<string>;
type NumberArray = Array<number>;
// Array by itself is a type constructor, not a type

لمحة عن رُتب الأنواع

كما أن للقيم أنواعًا، فلِمُنشئات الأنواع رُتب. لنوع عادي مثل number الرتبة *. أما Array فلها الرتبة * -> *: أعطها نوعًا واحدًا فتحصل على نوع. ويُسمى جعل هذه المُنشئات عامة «عالي الرتبة».

type ValueLike = number;        // kind *
// Array needs one argument:    kind * -> *
// Map needs two arguments:     kind * -> * -> *

الهدف: Functor<F>

في اللغات التي تدعم HKT، يمكنك كتابة تجريد واحد Functor<F> مع دالة map واحدة تعمل مع أي حاوية F، سواء كانت مصفوفة أو خيارًا أو Promise. واجهة واحدة وحاويات متعددة.

interface Wish_Functor<F> {
  map<A, B>(fa: F<A>, f: (a: A) => B): F<B>;
}
// F<A> here is the wish - and TypeScript rejects it

المشكلة

لا تدعم TypeScript تطبيق معلمة نوع على وسيط. لا يمكنك كتابة F<A> عندما تكون F معلمة عامة. ويصدر المصرّف الخطأ «Type F is not generic».

// interface Functor<F> {
//   map<A, B>(fa: F<A>, f: (a: A) => B): F<B>;
// }
// Error: Type F is not generic

لماذا تفتقر TS إلى ذلك

معلمات الأنواع في TypeScript هي من الرتبة الأولى: فهي تتعامل مع أنواع محددة، لا مع مُنشئات الأنواع. ولا توجد صياغة للتعبير عن أن F شيء يأخذ وسيط نوع واحدًا. لذلك لا معنى لـF<A> بالنسبة إلى المصرّف.

// You can pass number, string, User as F...
// but not Array, not Promise (the constructors themselves)

نقطة ألم ملموسة

من دون HKTs، يتعين عليكم تكرار map لكل حاوية: واحدة للمصفوفات، وأخرى للخيارات، وثالثة للوعود. المنطق متطابق، لكن نظام الأنواع لا يستطيع توحيدها.

declare function mapArray<A, B>(fa: A[], f: (a: A) => B): B[];
declare function mapOption<A, B>(fa: A | null, f: (a: A) => B): B | null;
// Same shape, but no way to abstract over the container

ما نريد التعبير عنه

نريد توقيعًا واحدًا: عند إعطاء حاوية من النوع A ودالة من A -> B، نعيد حاوية من النوع B، مع الحفاظ على نوع الحاوية. فتبقى المصفوفة مصفوفة، ويبقى الخيار خيارًا.

// Wish: map<F, A, B>(fa: F<A>, f: (a: A) => B): F<B>
// where F is preserved across the call

لغات أخرى

تدعم Haskell وScala الأنواع HKTs بشكل أصلي، ولذلك يمكن التعبير فيهما عن فئات الأنواع مثل Functor وApplicative وMonad. أما TypeScript فتحتاج إلى حل بديل لمحاكاة القدرة نفسها تقريبًا.

// Haskell: class Functor f where fmap :: (a -> b) -> f a -> f b
// TypeScript: needs an encoding trick (next lessons)

فكرة الترميز

الحل البديل الشائع هو إزالة التوظيف (defunctionalization): فبدلًا من تطبيق F مباشرةً، نمنح كل حاوية وسمًا نصيًا (URI)، ونحتفظ بسجل يربط الوسم مع وسيط بالنوع الفعلي. وهكذا يصبح تطبيق النوع مجرد بحث.

// Instead of F<A>, we write Kind<F, A> where F is a URI string
// and a registry resolves it to the real type

ما ستبنونه

على مدار الدروس التالية، ستبنون ترميز HKT بأسلوب fp-ts خطوةً خطوة: سجل URItoKind، وبحثًا باستخدام Kind، وأخيرًا Functor عامًا يعمل فيه map مع أي حاوية مسجّلة.

// Roadmap:
// 1. Type constructors via URI strings
// 2. Kind<F, A> resolution
// 3. Generic Functor + instances

لماذا تجدر معرفته

نادرًا ما تكتبون هذا الترميز بأنفسكم، لكن مكتبات مثل fp-ts وEffect تعتمد عليه. وفهم هذه الحيلة يتيح لكم قراءة أنواعها، وكتابة تجريدات عامة تعمل مع الحاويات، وتقدير ما يستطيع نظام الأنواع فعله وما لا يستطيع.

// Recognizing Kind<F, A> in library code is the payoff

تحقق سريع

اختبروا مدى فهمكم لمشكلة HKT.

مراجعة

تعرّفتم إلى مشكلة الأنواع عالية الرتبة.

  • تجرّد HKTs من منشئات الأنواع مثل Array، وليس من الأنواع الملموسة.
  • معاملات TypeScript من الرتبة الأولى، لذلك فإن F<A> غير قانوني.
  • يمنع ذلك إنشاء Functor عام واحد يتضمن map واحدة.
  • الحل هو ترميز يستخدم وسوم URI وسجلًا.

التالي: تعريف منشئات الأنواع باستخدام سلاسل URI.

الأسئلة الشائعة

هل درس «مشكلة HKT في TypeScript» مجاني؟

نعم — نص درس «مشكلة HKT في TypeScript» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة TypeScript Academy، انتقل إلى CoddyKit PRO. تتضمن دورة TypeScript Academy 4 دروس في المجموع.

ماذا ستتعلم في «مشكلة HKT في TypeScript»؟

افهموا سبب صعوبة مُنشئات الأنواع العامة تتمرن على TypeScript Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ TypeScript Academy؟

لا تُشترط خبرة سابقة. TypeScript Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.

كم من الوقت يستغرق درس «مشكلة HKT في TypeScript»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس TypeScript Academy هذا؟

نعم. كل درس في TypeScript Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. مشكلة HKT في TypeScript
  2. تعريف مُنشئات الأنواع
  3. نمط HKT خفيف الوزن
  4. Functors وMappers العامة
← العودة إلى TypeScript Academy