- كل المقالات
- الـ typealias يقبل وسيطًا نوعيًّا
الـ typealias يقبل وسيطًا نوعيًّا
تسمّي معالج الإكمال مرةً واحدة ثم تكتبه Handler<Post> في كل موضع. يعطيك ذلك وضوحًا وموضعًا واحدًا تغيّر فيه التوقيع. ولا يعطيك أمانًا نوعيًّا.
الـ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]>) { ... }
}
فبدونه يحمل كلُّ توقيعٍ من هذه (Result<Post, APIError>) -> Void كاملةً، وتصير
هيئة الواجهة — أنّ كل نداءٍ يعود بـResult بنوع الخطأ نفسه — شيئًا على القارئ
أن يجمعه من ثلاثة أسطرٍ يختلف ظاهرها قليلًا. ومعه تُقال الهيئة مرةً واحدة، ولا
يتغيّر إلا نوع الاستجابة.
وهو يمنحك كذلك موضعًا واحدًا للتغيير: فحين ينتقل نوع الخطأ من APIError إلى
غيره، ينتقل في سطرٍ واحد لا في كل توقيعٍ يذكره.
والاستعمال الآخر تقصيرُ اسمٍ لم تختره أنت:
final class Service {
typealias Book = SLBSomeLibraryServiceBookModel
func loadBooks() -> [Book] { ... }
}
وهذا آمنٌ داخل نوع، حيث يكون الاسم محصورًا ويجد القارئ موضع تعريفه. أما في نطاق
الملف فيحتاج إلى حذرٍ أكثر: فـBook من ثلاث وحداتٍ ثلاثةُ أشياء.
والذي لا يمنحك إياه شيءٌ من هذا هو الأمان النوعيّ. فـHandler<Post>
و(Result<Post, APIError>) -> Void نوعٌ واحد، لا نوعان اتّفق أن كُتبا كتابتين
— لا فرق يُلزمك به المترجم، ولا اختيار دالّةٍ يختلف بسببه. فإن أردت أن يعلم
المترجم أنّ إكمال هذه الخدمة ليس أيَّ closure، فالاسم المستعار أداةٌ خطأ،
والصواب نوعٌ يلفّها.
هو توثيقٌ يفحص المترجم هجاءه. وليس نوعًا.
كتب Antoine van der Lee مقالةً أطول عن الـtypealias تستحقّ عشر دقائق.