Menu
flag Ar iconالعربيةdown icon

أسئلة مقابلات TypeScript وإجاباتها (25 سؤالًا مع الكود)

25 سؤالًا من أسئلة مقابلات TypeScript مع إجابات قصيرة وصحيحة وكود، مرتبة من المبتدئ إلى المتقدم: any مقابل unknown، وinterface مقابل type، والـ generics، والتضييق، والأنواع المساعدة، وmapped types وconditional types، والأنواع البنيوية، وtsconfig، وTypeScript 7.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

هذه هي أسئلة TypeScript التي يطرحها المقابِلون أكثر من غيرها، مع الإجابة القصيرة التي ينبغي أن تستطيع تقديمها ومثال صغير. الأسئلة مرتبة حسب المستوى؛ مقابلة الوظيفة المبتدئة تتوقف عادة بعد المجموعة الأولى، ومقابلة الوظيفة المتقدمة تتوقع أن تكتب الأنواع في المجموعة الأخيرة من الذاكرة.

أسئلة المبتدئين

1. ما هو TypeScript؟

TypeScript هي JavaScript مع أنواع ثابتة، من تطوير Microsoft. تحدد أنواع القيم، ويفحصها المترجم، ثم يزيل الأنواع ويُخرج JavaScript عادية تعمل في أي مكان تعمل فيه JavaScript.

دون التعليق @ts-expect-error يكون الاستدعاء الثاني خطأ ترجمة، فلا يصل الخطأ البرمجي إلى الإنتاج أبدًا.

2. هل يفحص TypeScript الأنواع وقت التشغيل؟

لا. تُمحى الأنواع أثناء الترجمة؛ ولا يبقى لها أي أثر في الناتج. المثال السابق يطبع 23 لأنه لا شيء وقت التشغيل يعرف أن a يجب أن يكون رقمًا. لفحص البيانات القادمة من خارج البرنامج (JSON، ومدخلات المستخدم، واستجابات الواجهات البرمجية) تكتب فحوصًا حقيقية، مثل type guard أو أداة تحقق بمخطط.

3. ما الأنواع الأساسية؟

string، وnumber (نوع واحد للأعداد الصحيحة والعشرية؛ لا يوجد int)، وboolean، وbigint، وsymbol، وnull وundefined، إضافة إلى المصفوفات (number[])، والـ tuples ([string, number])، وأنواع الكائنات، وany وunknown وnever وvoid. استخدم الأسماء بالأحرف الصغيرة: String وNumber هما أنواع الكائنات المغلِّفة.

4. ما الفرق بين any وunknown؟

كلاهما يقبل أي قيمة. لكن any يعطّل الفحص أيضًا، فتُترجم أي عملية. أما unknown فلا يسمح بشيء حتى تضيّقه، وهذا يجعله النوع الآمن للقيم التي لم تفحصها بعد.

5. ما هو استنتاج الأنواع؟

يستنتج المترجم الأنواع التي لم تكتبها: let count = 0 من النوع number، وconst mode = "dark" من النوع الحرفي "dark"، ونوع إعادة الدالة يأتي من عبارات return فيها. القاعدة المعتادة هي تحديد أنواع معاملات الدوال وأنواع الإعادة العامة، وترك المتغيرات المحلية للاستنتاج.

6. ما الفرق بين interface وtype؟

كلاهما يصف أشكال الكائنات، ويستطيع الصنف أن يطبّق أيًا منهما بـ implements. يمكن إعادة فتح interface ودمجها (declaration merging)، وهي توسّع واجهات أخرى بـ extends. أما الاسم البديل type فيمكن أن يسمّي أي شيء: unions، وtuples، والأنواع الأولية، وmapped types وconditional types.

interface User { name: string }
interface User { age: number }       // merged: User has name and age

type Id = string | number;           // only a type alias can be a union
type Pair = [string, number];        // or a tuple
type Admin = User & { role: "admin" };

اصطلاح شائع هو interface لأشكال الكائنات وtype لكل ما عداها. المقارنة الكاملة في صفحة interface مقابل type.

7. ما أنواع union وintersection؟

الـ union A | B قيمة من أحد الأنواع؛ ولا تستطيع استخدام إلا الأعضاء المشتركة بينها كلها حتى تضيّقها. أما الـ intersection A & B فقيمة هي النوعان معًا، ولها كل أعضاء كل منهما.

type Id = string | number;                 // either
type Timestamped = { createdAt: Date };
type Post = { title: string } & Timestamped; // both: title and createdAt

8. ما الفرق بين void وnever؟

void هو نوع الإعادة لدالة تعود بشكل طبيعي دون قيمة مفيدة. وnever هو نوع شيء لا يمكن أن يحدث: دالة ترمي دائمًا أو تدور إلى الأبد، أو union أُزيلت كل حالاتها. يمكن إسناد never إلى كل نوع، ولا يمكن إسناد أي قيمة إلى never.

function log(msg: string): void { console.log(msg); }
function fail(msg: string): never { throw new Error(msg); }
type Impossible = string & number; // never

أسئلة المستوى المتوسط

9. ما هو تضييق النوع (narrowing)؟

التضييق هو تتبع المترجم لفحوصك وتدقيقه للنوع داخل كل فرع. يفهم typeof وinstanceof وin وفحوص المساواة والقيم truthy والـ type guards التي يعرّفها المستخدم.

10. ما هو الـ type guard الذي يعرّفه المستخدم؟

دالة نوع إعادتها value is T. عندما تعيد true يُضيَّق متغير المستدعي إلى T. يثق المترجم بالدالة، لذلك يجب أن يفحص جسمها القيمة فعلًا.

interface Cat { meow(): void }

function isCat(value: unknown): value is Cat {
    return (
        typeof value === "object" &&
        value !== null &&
        "meow" in value &&
        typeof value.meow === "function"
    );
}

دالة التأكيد، function assertCat(v: unknown): asserts v is Cat، تضيّق النوع برمي خطأ بدلًا من إعادة false.

11. ما هي الـ generics؟

معاملات أنواع تتيح لدالة أو صنف أو نوع واحد العمل مع أنواع كثيرة مع الحفاظ على الصلة بين المدخل والمخرج. يُستنتج T من الوسائط، ويقيّد extends ما يمكن أن يكونه.

سيكون pluck(users, "email") خطأ ترجمة، لأن "email" ليس من keyof نوع المستخدم.

12. ماذا يفعل keyof وtypeof في موضع النوع؟

keyof T هو union أسماء خصائص T. وفي موضع النوع يعطي typeof x نوع متغير. ومعًا يحوّل keyof typeof obj مفاتيح كائن إلى union.

const colors = { red: "#f00", green: "#0f0" };
type Colors = typeof colors;        // { red: string; green: string }
type ColorName = keyof typeof colors; // "red" | "green"

13. ما الأنواع المساعدة (utility types)؟ اذكر بعضها.

أنواع generic مدمجة تحوّل أنواعًا أخرى. الأكثر سؤالًا عنها:

النوع المساعدالنتيجة
Partial<T>كل الخصائص اختيارية
Required<T>كل الخصائص إلزامية
Readonly<T>كل الخصائص readonly
Pick<T, "a" | "b">الخصائص المذكورة فقط
Omit<T, "a">كل الخصائص عدا المذكورة
Record<K, V>كائن مفاتيحه K وقيمه V
Exclude<U, X>، Extract<U, X>حذف أعضاء union أو الإبقاء عليها
NonNullable<T>T دون null وundefined
ReturnType<F>، Parameters<F>نوع إعادة الدالة أو أنواع معاملاتها
Awaited<T>القيمة التي يُحل بها الـ promise

القائمة الكاملة في صفحة الأنواع المساعدة.

14. ما هي الـ discriminated union؟

union من أنواع كائنات تشترك في خاصية "وسم" حرفية. فحص الوسم يضيّق القيمة إلى عضو واحد، وفحص never في فرع default يجعل المترجم يبلّغ عن أي حالة تنساها.

15. ما الفرق بين as وsatisfies؟

value as T تأكيد نوع: يطلب من المترجم معاملة القيمة كـ T ويتخطى معظم الفحص. أما value satisfies T فيفحص القيمة مقابل T لكنه يحتفظ بنوع القيمة الخاص الأدق.

type Theme = { primary: string; secondary: string };

const a = { primary: "#07f" } as Theme;        // compiles: the missing key is not reported
const b = { primary: "#07f" } satisfies Theme; // error TS2741: Property 'secondary' is missing
const c = {} as { name: string };              // compiles; c.name is undefined at run time

وحين يكتمل الكائن، يحتفظ satisfies أيضًا بنوعه المستنتج: عند فحصه مقابل Record<string, string> يظل المتغير يعرف بالضبط مفاتيحه، بينما كان تحديد النوع سيوسّعه إلى أي مفتاح نصي.

16. ماذا يفعل المعامل ! بعد متغير؟

إنه تأكيد عدم القيمة الفارغة (non-null assertion): يزيل el! النوعين null وundefined من النوع. لا يولّد أي فحص، لذلك إذا كانت القيمة فعلًا null ينهار البرنامج حيث تُستخدم. فضّل ?. أو ?? أو if صريحًا.

17. ما الفرق بين private و#private؟

يفرض المترجم وحده private؛ فالخاصية في ناتج JavaScript خاصية عادية. أما #field فحقل خاص في JavaScript، يفرضه وقت التشغيل.

يعمل protected مثل private لكنه يسمح أيضًا بالوصول من الأصناف الفرعية، ويمنع readonly إعادة الإسناد بعد الإنشاء.

18. ما الفرق بين الصنف المجرد (abstract class) والواجهة؟

الواجهة نوع فقط: تصف شكلًا وتختفي من الناتج. أما الصنف المجرد فصنف حقيقي لا يمكن إنشاء نسخ منه؛ يمكن أن يحتوي على دوال منفذة وحقول ومُنشئات إلى جانب أعضاء abstract يجب أن تنفذها الأصناف الفرعية. يستطيع الصنف تطبيق واجهات كثيرة لكنه لا يرث إلا من صنف واحد.

abstract class Repository<T> {
    protected items: T[] = [];
    abstract validate(item: T): boolean; // subclasses must implement
    add(item: T): void {                 // shared implementation
        if (this.validate(item)) this.items.push(item);
    }
}

19. ما هو التحميل الزائد للدوال (function overloading) في TypeScript؟

عدة توقيعات استدعاء يليها تنفيذ واحد توقيعه متوافق معها كلها. لا يرى المستدعون إلا التوقيعات، فيحصل كل استدعاء على نوع إعادة دقيق.

function parse(value: string): number;
function parse(value: string[]): number[];
function parse(value: string | string[]): number | number[] {
    return Array.isArray(value) ? value.map(Number) : Number(value);
}

const one = parse("4");          // number
const many = parse(["1", "2"]);  // number[]

عندما لا يعتمد نوع الإعادة على نوع الوسيط، يكون التوقيع الواحد مع معامل union أبسط.

الأسئلة المتقدمة

20. ما هي الأنواع البنيوية (structural typing)؟

يقارن TypeScript الأنواع بشكلها لا باسمها. أي قيمة فيها الخصائص المطلوبة تُقبل، حتى لو لم تُعرّف قط بذلك النوع.

الخاصية الإضافية color لا مشكلة فيها هنا لأن pixel متغير. أما تمرير الكائن الحرفي مباشرة، show({ x: 3, y: 4, color: "red" })، فخطأ خاصية زائدة (excess property)، وهو فحص لا ينطبق إلا على الكائنات الحرفية الجديدة. عندما يجب ألا يختلط نوعان لهما الشكل نفسه (UserId وOrderId وكلاهما نص)، استخدم branded type: type UserId = string & { readonly __brand: "UserId" }.

21. كيف تنفذ Readonly وPartial بنفسك؟

باستخدام mapped types: كرر على keyof T وأضف معدِّلًا إلى كل خاصية.

type MyReadonly<T> = { readonly [K in keyof T]: T[K] };
type MyPartial<T> = { [K in keyof T]?: T[K] };
type Mutable<T> = { -readonly [K in keyof T]: T[K] }; // "-" removes a modifier

type User = { name: string; age: number };
type Draft = MyPartial<User>; // { name?: string; age?: number }

22. ما هي conditional types وinfer؟

يختار T extends U ? X : Y نوعًا بناءً على شرط. وداخل الشرط تعرّف infer متغير نوع يلتقط جزءًا من النوع المطابق. وتتوزع conditional types على الـ unions.

type MyReturnType<F> = F extends (...args: any[]) => infer R ? R : never;
type ElementOf<T> = T extends (infer E)[] ? E : T;

type A = MyReturnType<() => Promise<number>>; // Promise<number>
type B = ElementOf<string[]>;                 // string
type C = ElementOf<number | boolean[]>;       // number | boolean (distributed)

أنماط أخرى في صفحة conditional types.

23. ما هو ملف .d.ts، وماذا تفعل declare؟

ملف التعريفات يحتوي على أنواع فقط لكود موجود في مكان آخر، مثل مكتبة JavaScript أو واجهات المتصفح. وتصرّح declare بأن قيمة ما موجودة دون أن تنشئها: declare const VERSION: string; لا يُترجم إلى أي شيء. أنواع المكتبات تأتي مضمّنة في الحزمة أو من حزم @types/{name}، ويولّد tsc ملفات .d.ts لكودك مع declaration: true.

24. ماذا يفعل strict، وما أهم خيارات tsconfig؟

يفعّل "strict": true الخيارات noImplicitAny وstrictNullChecks وstrictFunctionTypes وstrictBindCallApply وstrictPropertyInitialization وnoImplicitThis وuseUnknownInCatchVariables وstrictBuiltinIteratorReturn. وفي TypeScript 7 يكون مفعّلًا افتراضيًا. الخيارات الأخرى التي يسأل عنها المقابِلون:

الخيارالإجابة في سطر واحد
targetإصدار JavaScript الذي يستخدمه الناتج
moduleصيغة وحدات الناتج: nodenext يتبع قواعد Node، وesnext/preserve يحتفظان بـ import لأداة التجميع
moduleResolutionكيف يُعثر على الاستيرادات: nodenext يطلب ./file.js في ملفات وحدات ES، وbundler يقبل ./file
noUncheckedIndexedAccessيتضمن نوع arr[i] القيمة undefined
pathsأسماء بديلة للاستيراد لمدقق الأنواع فقط؛ يحتفظ الناتج بالاسم البديل، لذلك يجب أن تحلّه أداة تجميع أو الحقل imports في Node
skipLibCheckتخطي فحص ملفات .d.ts، من أجل السرعة
verbatimModuleSyntaxالاستيرادات المستخدمة كأنواع فقط يجب تمييزها بـ type؛ وتبقى الاستيرادات الأخرى كما كُتبت

25. ما هو TypeScript 7؟

مترجم TypeScript وقد أُعيدت كتابته بلغة Go كبرنامج أصلي، وصدر كالإصدار 7 من حزمة npm المسماة typescript. ما زال الأمر tsc واللغة هي نفسها، لكنه أسرع بكثير من المترجم 6.x المبني على JavaScript: يذكر فريق TypeScript تسريعًا من 8 إلى 12 ضعفًا في عمليات البناء الكاملة المعتادة، بفضل الكود الأصلي وتعدد الخيوط بذاكرة مشتركة. ويفرض أيضًا الإزالات التي أعلن الإصدار 6.0 إهمالها؛ فمثلًا أصبحت baseUrl وoutFile وmoduleResolution: "node10" وmodule: "amd" وalwaysStrict: false أخطاء (TS5102، TS5108) تطلب منك إزالتها.

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

ما أكثر أسئلة مقابلات TypeScript شيوعًا؟

الأكثر تكرارًا: الفرق بين any وunknown، وinterface مقابل type، وكيف تعمل الـ generics، وكيف يعمل التضييق والـ type guards، وما تفعله الأنواع المساعدة مثل Partial وPick وOmit، وهل يفحص TypeScript الأنواع وقت التشغيل (لا يفعل).

ما أسئلة TypeScript التي تُطرح على المطورين ذوي الخبرة؟

توقّع أن تكتب الأنواع لا أن تقرأها فقط: تنفيذ Readonly أو ReturnType بـ mapped types وconditional types، وشرح الأنواع البنيوية ومتى تستخدم branded types، وتصميم discriminated union مع فحص شامل، ومناقشة خيارات tsconfig مثل strict وnoUncheckedIndexedAccess وتحليل الوحدات.

كيف أستعد لمقابلة TypeScript؟

اكتب برامج صغيرة مع تفعيل strict واقرأ أخطاء المترجم حتى تستطيع توقعها. كن قادرًا على شرح التضييق، والـ generics مع القيود، والأنواع المساعدة الرئيسية، ولماذا تأكيدات النوع غير آمنة، مع مثال من سطرين لكل منها.

هل يُسأل عن TypeScript في مقابلات React وAngular؟

عادةً نعم. Angular نفسه مكتوب بـ TypeScript وتطبيقاته تُكتب بها، لذلك تفترض مقابلات Angular معرفتها غالبًا، بما في ذلك الـ decorators مثل @Component ومعدِّلات الوصول. ومقابلات React لقاعدة كود TypeScript تطلب منك غالبًا تحديد أنواع الـ props والحالة ومعالجات الأحداث، وهذا يستخدم الواجهات والـ unions والـ generics نفسها المعروضة هنا.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن