אלה השאלות על TypeScript שמראיינים שואלים הכי הרבה, עם התשובה הקצרה שכדאי שתדעו לתת ודוגמה קטנה. הן מסודרות לפי רמה; בתפקיד ג'וניור בדרך כלל עוצרים אחרי הקבוצה הראשונה, ובתפקיד בכיר מצפים שתכתבו מהזיכרון את הטיפוסים בקבוצה האחרונה.
שאלות למתחילים
1. מה זה TypeScript?
TypeScript היא JavaScript עם טיפוסים סטטיים, שפותחה ב-Microsoft. אתם מוסיפים לערכים הערות טיפוס, הקומפיילר בודק אותן, ואז הוא מסיר את הטיפוסים ומוציא JavaScript רגיל שרץ בכל מקום שבו JavaScript רץ.
בלי ההערה @ts-expect-error, הקריאה השנייה היא שגיאת קומפילציה, כך שהבאג אף פעם לא מגיע לפרודקשן.
2. האם TypeScript בודק טיפוסים בזמן ריצה?
לא. הטיפוסים נמחקים בזמן הקומפילציה; בפלט אין מהם שום זכר. ההרצה למעלה מדפיסה 23 כי שום דבר בזמן ריצה לא יודע ש-a אמור להיות מספר. כדי לבדוק נתונים שמגיעים מחוץ לתוכנית (JSON, קלט של משתמשים, תשובות של API) כותבים בדיקות אמיתיות, כמו 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. מה זה הסקת טיפוסים (type inference)?
הקומפיילר מחשב טיפוסים שלא כתבתם: let count = 0 הוא number, const mode = "dark" הוא טיפוס הליטרל "dark", וטיפוס ההחזרה של פונקציה נקבע מפקודות ה-return שלה. הכלל הרגיל הוא לכתוב טיפוסים לפרמטרים של פונקציות ולטיפוסי החזרה ציבוריים, ולתת להסקה לטפל במשתנים מקומיים.
6. מה ההבדל בין interface ל-type?
שניהם מתארים מבנים של אובייקטים, ומחלקה יכולה לעשות implements לכל אחד מהם. אפשר לפתוח מחדש interface ולמזג אותו (declaration merging), והוא יכול לעשות extends ל-interfaces אחרים. type alias יכול לתת שם לכל דבר: 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, בדיקות שוויון, truthiness ו-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"
);
}
assertion 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? תנו כמה דוגמאות.
טיפוסים גנריים מובנים שמשנים טיפוסים אחרים. אלה ששואלים עליהם הכי הרבה:
| Utility | תוצאה |
|---|---|
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 נפתר אליו |
הרשימה המלאה נמצאת בעמוד על utility types.
14. מה זה discriminated union?
union של טיפוסי אובייקט שחולקים מאפיין "תג" מסוג ליטרל. בדיקה של התג מצמצמת את הערך לאיבר אחד, ובדיקת never בענף ה-default גורמת לקומפיילר לדווח על כל מקרה ששכחתם.
15. מה ההבדל בין as ל-satisfies?
value as T הוא type assertion: הוא אומר לקומפיילר להתייחס לערך כאל 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. מה ההבדל בין מחלקה אבסטרקטית ל-interface?
interface הוא רק טיפוס: הוא מתאר מבנה ונעלם מהפלט. מחלקה אבסטרקטית היא מחלקה אמיתית שאי אפשר ליצור ממנה מופע; היא יכולה להחזיק מתודות ממומשות, שדות ובנאים לצד איברים abstract שתתי-מחלקות חייבות לממש. מחלקה יכולה לממש הרבה interfaces אבל להרחיב רק מחלקה אחת.
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?
כמה חתימות קריאה ואחריהן מימוש אחד שהחתימה שלו תואמת לכולן. מי שקורא לפונקציה רואה רק את ה-overloads, ולכן כל קריאה מקבלת טיפוס החזרה מדויק.
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" }), היא שגיאת מאפיין עודף, בדיקה שחלה רק על ליטרלים חדשים. כששני טיפוסים עם אותו מבנה לא אמורים להתערבב (UserId ו-OrderId ששניהם מחרוזות), השתמשו ב-branded type: type UserId = string & { readonly __brand: "UserId" }.
21. איך הייתם מממשים בעצמכם את Readonly ואת Partial?
עם mapped types: עוברים על keyof T ומוסיפים modifier לכל מאפיין.
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 או ממשקי API של הדפדפן. 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 בשביל bundler |
moduleResolution | איך imports נמצאים: nodenext דורש ./file.js בקובצי ES module, ו-bundler מקבל ./file |
noUncheckedIndexedAccess | arr[i] כולל undefined בטיפוס שלו |
paths | כינויים ל-imports רק בשביל בודק הטיפוסים; הפלט שומר את הכינוי, ולכן bundler או שדה imports של Node חייבים לפתור אותו |
skipLibCheck | דילוג על בדיקת קובצי .d.ts, בשביל מהירות |
verbatimModuleSyntax | imports שמשמשים רק כטיפוסים חייבים להיות מסומנים ב-type; imports אחרים נשמרים כפי שנכתבו |
25. מה זה TypeScript 7?
הקומפיילר של TypeScript שנכתב מחדש ב-Go כתוכנה נייטיבית, ושוחרר כגרסה 7 של חבילת ה-npm typescript. הפקודה היא עדיין tsc והשפה זהה, אבל הוא מהיר הרבה יותר מהקומפיילר של 6.x שמבוסס על JavaScript: צוות TypeScript מדווח על האצה של פי 8 עד פי 12 ב-builds מלאים טיפוסיים, בזכות קוד נייטיבי ו-multithreading עם זיכרון משותף. הוא גם אוכף הסרות ש-6.0 סימנה כ-deprecated; למשל baseUrl, outFile, moduleResolution: "node10", module: "amd" ו-alwaysStrict: false הם עכשיו שגיאות (TS5102, TS5108) שאומרות לכם להסיר אותם.
שאלות נפוצות
מהן השאלות הנפוצות ביותר בראיונות על TypeScript?
אלה שעולות הכי הרבה: ההבדל בין any ל-unknown, interface מול type, איך generics עובדים, איך עובדים צמצום טיפוסים ו-type guards, מה utility types כמו Partial, Pick ו-Omit עושים, והאם TypeScript בודק טיפוסים בזמן ריצה (הוא לא).
אילו שאלות TypeScript שואלים מפתחים בכירים?
צפו לכתוב טיפוסים, לא רק לקרוא אותם: לממש Readonly או ReturnType עם mapped types ו-conditional types, להסביר טיפוסיות מבנית ומתי להשתמש ב-branded types, לתכנן discriminated union עם בדיקה ממצה, ולדון בבחירות של tsconfig כמו strict, noUncheckedIndexedAccess ו-module resolution.
איך מתכוננים לראיון על TypeScript?
כתבו תוכניות קטנות כש-strict פועל וקראו את שגיאות הקומפיילר עד שתוכלו לצפות אותן. היו מסוגלים להסביר צמצום טיפוסים, generics עם אילוצים, את ה-utility types העיקריים, ולמה type assertions אינם בטוחים, כל אחד עם דוגמה של שתי שורות.
האם שואלים על TypeScript בראיונות ל-React ול-Angular?
בדרך כלל. Angular עצמה כתובה ב-TypeScript ואפליקציות Angular נכתבות בה, ולכן ראיונות ל-Angular נוטים להניח שאתם מכירים אותה, כולל decorators כמו @Component ו-access modifiers. ראיונות ל-React בבסיס קוד של TypeScript מבקשים לעתים קרובות לתת טיפוסים ל-props, ל-state ול-event handlers, וזה משתמש באותם interfaces, unions ו-generics שמוצגים כאן.