אינטרפייסים וטייפים בטייפסקריפט¶
ברגע שמתחילים לכתוב טייפסקריפט אמיתי, מגלים שיש שתי דרכים שנראות דומות מאוד להגדרת "צורה" של אובייקט - interface ו-type. זה בלבול נפוץ, כי במקרים רבים אפשר להשתמש בכל אחת מהן ולקבל אותה תוצאה. בואו נבין את ההבדלים, ומתי כדאי לבחור בכל אחת.
interface - הגדרת צורת אובייקט¶
interface מגדיר איך אמור להיראות אובייקט - אילו שדות יש לו, ומאיזה טיפוס כל שדה.
interface User {
id: number;
name: string;
email: string;
}
const user: User = {
id: 1,
name: "טל",
email: "tal@example.com",
};
אם תנסו ליצור אובייקט שלא תואם בדיוק את ה-interface - חסר שדה, או שדה מהטיפוס הלא נכון - טייפסקריפט יתריע על כך מיד בעורך הקוד, עוד לפני שהקוד בכלל רץ.
שדות אופציונליים ושדות readonly¶
לא כל שדה חייב להתקיים תמיד. סימן שאלה אחרי שם השדה הופך אותו לאופציונלי:
interface User {
id: number;
name: string;
email: string;
phone?: string; // אופציונלי - לא חייב להיות שם
}
const user: User = { id: 1, name: "טל", email: "tal@example.com" };
// תקין, למרות שאין phone
readonly מבטיח ששדה לא ישתנה אחרי שהאובייקט נוצר:
interface Config {
readonly apiKey: string;
}
const config: Config = { apiKey: "abc123" };
config.apiKey = "xyz"; // שגיאה - אי אפשר לשנות שדה readonly
type alias - כלי גמיש יותר¶
type יכול לעשות בערך את אותו דבר כמו interface:
אבל type יכול גם דברים ש-interface לא יכול - להגדיר טיפוסי איחוד, טיפוסים פרימיטיביים עם שם, וטיפוסים מורכבים יותר:
לא אפשר לכתוב interface Status = "pending" | "completed" - interface מוגבל לצורת אובייקטים בלבד, בעוד type יכול לתאר כל טיפוס אפשרי.
הרחבת interface מול הרחבת type¶
interface מתרחב עם extends, ומאפשר לבנות היררכיה ברורה:
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
const rex: Dog = { name: "רקס", breed: "לברדור" };
type משיג תוצאה דומה עם intersection (&):
ההבדל שכן חשוב - declaration merging¶
יש הבדל טכני אחד שכדאי להכיר - interface עם אותו שם, כשמוגדר פעמיים, מתמזג אוטומטית לשדות אחד:
interface Window {
myCustomProperty: string;
}
interface Window {
anotherProperty: number;
}
// Window עכשיו מכיל את שני השדות
זה שימושי בעיקר כשמרחיבים טיפוסים של ספריות חיצוניות. type לא תומך בהתנהגות הזאת - הגדרה כפולה של אותו שם type היא שגיאה.
אז מתי להשתמש בכל אחד - כלל אצבע פשוט¶
בפועל, בפרויקטי ריאקט, ההמלצה הנפוצה היא - interface לצורות של אובייקטים, במיוחד props של קומפוננטות, ו-type לכל דבר אחר - איחודים, טיפוסים מורכבים, או כינויים לטיפוסים פרימיטיביים.
interface ButtonProps {
label: string;
onClick: () => void;
variant?: "primary" | "secondary";
}
function Button({ label, onClick, variant = "primary" }: ButtonProps) {
return <button onClick={onClick}>{label}</button>;
}
אבל חשוב לדעת - זו המלצת סגנון, לא כלל טכני מחייב. צוותים רבים פשוט בוחרים type לכל דבר, כדי לא להצטרך לחשוב כל פעם מחדש איזה מהשניים מתאים. העיקר הוא עקביות בתוך הפרויקט, לא הבחירה הספציפית.
למה בכלל להטריח את עצמכם עם זה¶
מי שמגיע מג'אווהסקריפט רגיל שואל למה להוסיף את כל השכבה הזאת. התשובה - ברגע שיש לכם interface או type שמתאר בדיוק מה קומפוננטה מצפה לקבל, עורך הקוד מזהיר אתכם מיד אם שכחתם שדה חובה, או העברתם טיפוס שגוי. זה חוסך המון שעות דיבוג של "למה הקומפוננטה מקבלת undefined".
בקורס צד לקוח יש פרק שלם על טייפסקריפט בריאקט, כולל תרגול מעשי על הגדרת props נכונה לקומפוננטות אמיתיות.
לסיכום¶
interface ו-type שניהם מגדירים "צורה" של נתונים, ורוב הזמן אפשר להשתמש בכל אחד מהם. interface עדיף לצורות אובייקטים ותומך בהרחבה עם extends ומיזוג הגדרות, ו-type גמיש יותר ותומך גם באיחודים וטיפוסים מורכבים. הבחירה בין השניים חשובה פחות מהעובדה שאתם בכלל מגדירים טיפוסים ברורים.
מתלבטים בין interface ל-type בקוד שלכם? תשאלו בדיסקורד.