إتقان JavaScript و TypeScript
أنماط TypeScript في المقابلات
أصبح TypeScript الآن متطلبًا أساسيًا لمعظم أدوار الواجهات الأمامية. تختبر المقابلات قدرتك على استخدام نظام الأنواع لكتابة كود أكثر أمانًا وقابلية للصيانة — وليس مجرد إضافة تعليقات : string.
قبل أنواع المساعدة، إليك السؤال الأرجح أن يُطرح عليك أولًا، والذي يجيب عنه معظم المرشحين بهزّة كتف:
`interface` أم `type` — الإجابة التي ليست «هما نفس الشيء تقريبًا»
interface
- دمج التصريحات يتيح لمستهلكي مكتبتك توسيع شكل نشرته — وهكذا تُضاف حقول إلى أنواع مكتبة دون نسخها
- استخدام `implements` على فئة يُقرأ بشكل طبيعي، ورسائل الخطأ حين تخرق الفئة العقد أوضح
- توسيع عدة واجهات يُفحص مبكرًا، فتظهر التعارضات عند التصريح لا عند الاستخدام
- لا يستطيع التعبير عن اتحاد أو صف (tuple) أو نوع مُعيَّن (mapped) أو نوع شرطي إطلاقًا
- دمج التصريحات صامت — ملفان قد يوسّعان نفس الواجهة دون أن ينتبه أحد، وهي ميزة في مكتبة ومخاطرة في تطبيق
- لا سبيل لتسمية نوع بدائي، فـ `type UserId = string` ليس له مكافئ بالواجهات
type
- الاتحادات والتقاطعات والصفوف والأنواع المُعيَّنة والشرطية لا يمكن التعبير عنها إلا هنا — وكل نوع مساعدة في القسم التالي هو `type`
- إعادة التصريح خطأ في وقت الترجمة، فلا يمكن توسيع النوع من وراء ظهرك
- يتركّب مع `keyof` و`typeof` والقوالب النصية، وهناك يجري العمل المثير على مستوى الأنواع
- لا دمج تصريحات، فمستهلكو النوع المنشور لا يستطيعون توسيعه — عليهم تغليفه
- الأسماء المركّبة بعمق تُنتج رسائل خطأ تتمدد إلى بنية غير قابلة للقراءة
- لا شيء يمنعك من كتابة نوع بارع لا يستطيع أحد في الفريق تعديله بعد ستة أشهر
الجملة التي تقولها بصوت عالٍ: «واجهة حين يكون الشكل عقدًا قد يوسّعه آخرون، ونوعًا حين أحسب نوعًا من نوع آخر. في كود التطبيقات نادرًا ما يؤثر الفرق، فأتبع ما تفعله قاعدة الكود أصلًا.» تلك الجملة الأخيرة هي ما يفصل الإجابة عن الاستظهار.
أنواع المساعدة (Utility Types)
هذه الأنواع المدمجة تظهر باستمرار في المقابلات. احفظها عن ظهر قلب:
interface User {
id: number;
name: string;
email: string;
role: 'admin' | 'user' | 'guest';
createdAt: Date;
}
// Pick: اختيار خصائص محددة
type UserPreview = Pick<User, 'id' | 'name'>;
// { id: number; name: string }
// Omit: استبعاد خصائص محددة
type UserWithoutDates = Omit<User, 'createdAt'>;
// { id: number; name: string; email: string; role: ... }
// Partial: جعل جميع الخصائص اختيارية
type UpdateUser = Partial<User>;
// { id?: number; name?: string; email?: string; ... }
// Required: جعل جميع الخصائص مطلوبة
type CompleteUser = Required<Partial<User>>;
// العودة لكل الخصائص مطلوبة
// Record: إنشاء نوع كائن بأنواع مفتاح-قيمة محددة
type RolePermissions = Record<User['role'], string[]>;
// { admin: string[]; user: string[]; guest: string[] }
// Readonly: جعل جميع الخصائص للقراءة فقط
type FrozenUser = Readonly<User>;
// لا يمكن إعادة تعيين أي خصائص
الأنواع العامة (Generics)
الأنواع العامة تتيح لك كتابة كود قابل لإعادة الاستخدام وآمن الأنواع:
// دالة عامة أساسية
function getFirst<T>(arr: T[]): T | undefined {
return arr[0];
}
getFirst([1, 2, 3]); // number
getFirst(['a', 'b']); // string
// عام مع قيود
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user: User = { id: 1, name: 'Alice', email: 'a@b.com', role: 'admin', createdAt: new Date() };
getProperty(user, 'name'); // string
getProperty(user, 'role'); // 'admin' | 'user' | 'guest'
// getProperty(user, 'age'); // خطأ: 'age' ليس في keyof User
الأنواع الشرطية (Conditional Types)
هذه قوية لبناء أدوات أنواع مرنة:
// نوع شرطي أساسي
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// استخراج الأنواع غير القابلة للإلغاء
type NonNullable<T> = T extends null | undefined ? never : T;
type C = NonNullable<string | null | undefined>; // string
// كلمة infer: استخراج الأنواع من أنواع أخرى
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type D = ReturnType<() => string>; // string
type E = ReturnType<(x: number) => boolean>; // boolean
// مثال عملي: استخراج النوع الداخلي لـ Promise
type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;
type F = Awaited<Promise<string>>; // string
type G = Awaited<Promise<Promise<number>>>; // number
الاتحادات المميزة (Discriminated Unions)
نمط يظهر بشكل متكرر في خصائص مكونات React:
// بدلاً من خصائص اختيارية مع شروط معقدة،
// استخدم الاتحادات المميزة
type ButtonProps =
| { variant: 'link'; href: string; onClick?: never }
| { variant: 'button'; onClick: () => void; href?: never }
| { variant: 'submit'; form: string; href?: never; onClick?: never };
function Button(props: ButtonProps) {
switch (props.variant) {
case 'link':
return <a href={props.href}>Link</a>;
case 'button':
return <button onClick={props.onClick}>Click</button>;
case 'submit':
return <button type="submit" form={props.form}>Submit</button>;
}
}
// TypeScript يضيّق النوع في كل فرع حالة
// props.href يمكن الوصول إليه فقط عندما يكون variant هو 'link'
حراس الأنواع (Type Guards)
حراس الأنواع المخصصة تنقّح الأنواع في وقت التشغيل:
// دالة توقع النوع مع `is`
function isString(value: unknown): value is string {
return typeof value === 'string';
}
// استخدام عامل `in`
interface Dog { bark(): void; }
interface Cat { meow(): void; }
function isDog(pet: Dog | Cat): pet is Dog {
return 'bark' in pet;
}
// التحقق الشامل مع `never`
type Shape = { kind: 'circle'; radius: number }
| { kind: 'square'; side: number }
| { kind: 'triangle'; base: number; height: number };
function area(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'square':
return shape.side ** 2;
case 'triangle':
return 0.5 * shape.base * shape.height;
default:
// إذا تمت إضافة شكل جديد ولم يُعالج،
// سيعطي TypeScript خطأ هنا في وقت الترجمة
const _exhaustive: never = shape;
return _exhaustive;
}
}
أنواع القوالب الحرفية (Template Literal Types)
مفيدة لإنشاء أنواع سلاسل نصية دقيقة:
type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type APIPath = '/users' | '/posts' | '/comments';
// الجمع في سلاسل مسارات
type APIRoute = `${HTTPMethod} ${APIPath}`;
// 'GET /users' | 'GET /posts' | ... (12 تركيبة)
// عملي: أنواع خصائص CSS
type CSSUnit = 'px' | 'rem' | 'em' | '%' | 'vh' | 'vw';
type CSSValue = `${number}${CSSUnit}`;
const width: CSSValue = '100px'; // صالح
const height: CSSValue = '50rem'; // صالح
// const bad: CSSValue = 'auto'; // خطأ
نصيحة للمقابلات: عندما يُطلب منك "تحسين أمان الأنواع" لجزء من الكود، ابحث عن فرص لاستخدام الاتحادات المميزة والأنواع العامة مع القيود وأنواع القوالب الحرفية. هذه تُظهر فهمًا عميقًا لـ TypeScript يتجاوز التعليقات التوضيحية الأساسية.
التالي: سنغطي الأنماط غير المتزامنة ومعالجة الأخطاء — الموضوع الرئيسي الآخر في JavaScript الذي يُعثّر المرشحين. :::
سجّل الدخول للتقييم