مقابلة متقدمة في TypeScript: كيف تُبهر صاحب العمل
لقد توقف TypeScript منذ فترة طويلة عن كونه مجرد "إضافة" فوق JavaScript. في المقابلات الحديثة للمناصب العليا (Senior)، لا يختبرون فقط معرفة الأنواع، بل أيضًا القدرة على العمل مع الأنواع الشرطية، والتعيين، والمُزينات، ونظام استنتاج الأنواع. في هذه المقالة، سنستعرض 4 مواضيع رئيسية ستساعدك على اجتياز المقابلة الفنية في TypeScript بمستوى متقدم بثقة.
1. الأنواع الشرطية (Conditional Types) والكلمة المفتاحية infer
تسمح الأنواع الشرطية باختيار أحد نوعين بناءً على شرط. تُبنى على مبدأ العامل الثلاثي: T extends U ? X : Y. في المقابلة، قد يُطلب منك كتابة نوع يستخرج نوع الإرجاع من دالة.
// مثال: استخراج نوع الإرجاع من دالةtype ReturnTypeExtract<T> = T extends (...args: any[]) => infer R ? R : never;
function greet(name: string): string { return "Hello, " + name;}
type GreetReturn = ReturnTypeExtract<typeof greet>; // stringتُستخدم الكلمة المفتاحية infer داخل الأنواع الشرطية لتعريف متغير نوع جديد. تسمح بـ "استخراج" جزء من النوع من بنية معقدة. غالبًا ما يُسأل: "ما هو infer وأين يُستخدم؟". الإجابة: لاستخراج الأنواع من Promises، والدوال، والمجموعات (tuples).
// استخراج نوع العنصر من Promisetype UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type Result = UnwrapPromise<Promise<number>>; // numbertype Result2 = UnwrapPromise<string>; // string2. الأنواع المُعيّنة (Mapped Types) ومُعدّلات readonly/optional
تسمح الأنواع المُعيّنة بإنشاء أنواع جديدة عن طريق تكرار خصائص نوع موجود. هذه أداة قوية لإنشاء أنواع مساعدة مثل Partial<T> أو Readonly<T>. في المقابلة، قد يُطلب منك تنفيذ نسختك الخاصة من Pick<T, K>.
// تنفيذ خاص لـ Picktype MyPick<T, K extends keyof T> = { [P in K]: T[P];};
interface User { id: number; name: string; email: string;}
type UserBasic = MyPick<User, 'id' | 'name'>;// { id: number; name: string }لاحظ keyof T — وهو عامل يُرجع اتحاد مفاتيح الكائن. بالدمج مع التعيين، يسمح بتحويل الأنواع بمرونة. أيضًا، غالبًا ما يُسأل عن المُعدّلات readonly و ? (اختياري). يمكن إضافتها أو إزالتها باستخدام البادئات + و -:
// إزالة readonly من جميع الخصائصtype Mutable<T> = { -readonly [P in keyof T]: T[P];};
type ImmutableUser = Readonly<User>;type MutableUser = Mutable<ImmutableUser>; // جميع الحقول قابلة للتغيير مرة أخرى3. المُزينات (Decorators) وكتابة أنواعها
المُزينات هي ميزة تجريبية في TypeScript تُستخدم بنشاط في Angular و NestJS. في المقابلة، قد يُسأل عن كيفية كتابة نوع لمُزين كلاس أو دالة. من المهم فهم أن المُزين هو مجرد دالة تستقبل الكائن الهدف وتُرجعه (أو تُرجع كائنًا جديدًا).
// مُزين كلاس مع كتابة النوعfunction Logger<T extends { new (...args: any[]): {} }>(constructor: T) { console.log(`Class ${constructor.name} created`); return class extends constructor { constructor(...args: any[]) { super(...args); console.log('Instance created'); } };}
@Loggerclass MyService { constructor(public name: string) {}}
const service = new MyService('Test'); // في وحدة التحكم: "Class MyService created"، ثم "Instance created"في الأسئلة المتقدمة، قد يُطلب تنفيذ مُزين يُضيف بيانات وصفية أو تحققًا. لاحظ استخدام الأدوية (Generics) — <T extends { n