ملاحظةدقيقتا قراءة

استخدام الأنواع العامة مع typealias في Swift

استخدم typealias لتسمية دوال الإكمال وتوحيد توقيعاتها، مع تغيير نوع الاستجابة حسب الحاجة. اسم أوضح للشيفرة، دون إنشاء نوع جديد.

عرض بصيغة Markdown

يعطي typealias اسمًا بديلًا لنوع موجود. مثلًا، TimeInterval اسم بديل لـ Double يوضح أن القيمة تمثل مدة زمنية.

ويمكن أن يأخذ الاسم البديل وسيطًا نوعيًا. أستخدم ذلك كثيرًا لتسمية دوال الإكمال:

struct Service {
  typealias Handler<Response> = (Result<Response, APIError>) -> Void

  func fetchPost(id: Int, _ completion: @escaping Handler<Post>) { ... }
  func fetchPosts(_ completion: @escaping Handler<[Post]>) { ... }
  func fetchBooks(_ completion: @escaping Handler<[Book]>) { ... }
}

بدون Handler، نكرر توقيع الدالة (Result<Post, APIError>) -> Void مع تغيير نوع الاستجابة في كل مرة. الاسم البديل يوضح الجزء المشترك: كل دالة إكمال تستقبل Result، ونوع الخطأ واحد في جميع الحالات.

إذا تغير نوع الخطأ، يكفي تعديل تعريف Handler بدل البحث عن كل توقيع يذكر APIError.

ويمكن استخدامه أيضًا لاختصار اسم طويل من مكتبة خارجية:

final class Service {
  typealias Book = SLBSomeLibraryServiceBookModel

  func loadBooks() -> [Book] { ... }
}

داخل Service، يبقى معنى Book واضحًا ويمكن العثور على تعريفه بسهولة. أما الأسماء المختصرة في نطاق أوسع فقد تربك القارئ، خصوصًا إذا كانت عدة وحدات تستخدم الاسم نفسه لأنواع مختلفة.

لكن الاسم الجديد لا ينشئ نوعًا جديدًا. Handler<Post> و(Result<Post, APIError>) -> Void هما النوع نفسه بالنسبة للمترجم، ويمكن استخدام أحدهما مكان الآخر. إذا أردت تمييز دالة الإكمال الخاصة بخدمتك عن غيرها، فستحتاج إلى نوع مستقل يغلّفها.

فائدة typealias هنا في وضوح الواجهة وسهولة تعديلها؛ أما الفصل بين الأنواع فيحتاج إلى أداة أخرى.

لدى Antoine van der Lee شرح أوسع لاستخدام typealias مع أمثلة إضافية.

هل تستخدم typealias جعل واجهة معقدة أسهل في القراءة؟ يسعدني أن أرى مثالك. ويمكنك مشاركة هذه الملاحظة مع زميل يبحث عن أسماء أوضح في شيفرته.

المواضيع