Type inference הוא TypeScript שמסיק טיפוס שלא כתבתם. אתחלו משתנה והוא יקבל את הטיפוס של הערך; החזירו ערך מפונקציה וטיפוס ההחזרה יגיע בעקבותיו. ברוב קוד ה-TypeScript יש הרבה פחות הערות טיפוס ממה שאנשים מצפים.
ההערות מראות מה עורך הקוד מציג כשמרחפים מעל כל שם. השורה האחרונה היא שגיאת קומפילציה (TS2322) למרות שאף פעם לא נכתב טיפוס ל-count; // @ts-expect-error מסמן אותה כצפויה, כך ששאר הבלוק רץ.
let מול const: הרחבה של ליטרלים
const לא יכול להשתנות אף פעם, ולכן TypeScript נותן לו את הטיפוס הצר ביותר: הערך הליטרלי עצמו. let מקבל את הטיפוס הרחב יותר, כי ייתכן שישימו לו ערך אחר מאוחר יותר.
let a = "left"; // string
const b = "left"; // "left"
let c = 42; // number
const d = 42; // 42
const e = true; // true
זה חשוב כשפונקציה מקבלת רק ערכים מסוימים:
index.ts(7,6): error TS2345: Argument of type 'string' is not assignable to parameter of type '"left" | "right"'.
תקנו עם const, או עם הערת טיפוס על המשתנה: let dir: "left" | "right" = "left". לטיפוסים ליטרליים יש עמוד משלהם, literal types.
אובייקטים ומערכים מרחיבים את האיברים שלהם
מאפיינים של אובייקט ליטרלי ניתנים לשינוי, ולכן הם מורחבים כמו let, גם כשהאובייקט עצמו הוא const. איברי מערך מתנהגים אותו דבר. as const הופך הכול לקריאה בלבד ושומר על הטיפוסים הליטרליים.
הסקת טיפוס ההחזרה
טיפוס ההחזרה של פונקציה מוסק מפקודות ה-return שלה. כשיש כמה, התוצאה היא ה-union שלהן.
טיפוסי החזרה שהוסקו מתאימים לרוב הפונקציות. כתבו את טיפוס ההחזרה במפורש בפונקציות מיוצאות או ציבוריות כשאתם רוצים שהחתימה תהיה חוזה: שינוי בגוף הפונקציה שמחזיר טיפוס אחר ייכשל אז בפונקציה עצמה, ולא אצל קורא רחוק כלשהו.
Contextual typing: callbacks מקבלים את הטיפוסים שלהם
פרמטרים לא מוסקים מהאופן שבו קוראים לפונקציה. יש שני חריגים. פרמטר עם ערך ברירת מחדל מקבל את הטיפוס של ברירת המחדל (function retry(times = 3) הופך את times ל-number). ופונקציה שנכתבת במקום שבו כבר מצופה טיפוס של פונקציה מקבלת את טיפוסי הפרמטרים שלה מההקשר הזה, ולכן פרמטרים של callbacks לא צריכים הערת טיפוס.
אותו דבר חל על forEach, filter, reduce, callbacks של Promise, מטפלי אירועים וכל פונקציה שמעבירים לפרמטר עם טיפוס פונקציה מוצהר.
פרמטרים צריכים הערות טיפוס
בלי ערך ברירת מחדל או הקשר כזה, TypeScript לא מסתכל על האופן שבו קוראים לפונקציה כדי לנחש את טיפוסי הפרמטרים שלה. כש-strict מופעל, פרמטר בלי טיפוס הוא השגיאה TS7006:
index.ts(2,17): error TS7006: Parameter 'x' implicitly has an 'any' type.
כתבו function double(x: number) והבלוק ידפיס 42, עם טיפוס ההחזרה number שמוסק מ-x * 2.
Best common type
עבור מערך ליטרלי עם סוגים שונים של ערכים, TypeScript מסיק מערך של ה-union של הטיפוסים שלהם. הוא לא מחפש טיפוס בסיס משותף שלא ציינתם.
מערך ריק הוא המקרה היחיד שצריך לשים לב אליו. const list = [] מתחיל כמערך "מתפתח" שהטיפוס שלו גדל עם כל push; הערת טיפוס, const list: string[] = [], מצהירה על הכוונה מראש וברורה יותר.
מתי לכתוב טיפוס
| מצב | לכתוב טיפוס? |
|---|---|
| משתנה שמאותחל עם ערך | לא, הערך נותן את הטיפוס |
משתנה שמוצהר בלי ערך (let result;) | כן, let result: string; |
| פרמטרים של פונקציה | כן (חוץ מ-callbacks עם הקשר, או ערך ברירת מחדל) |
| טיפוס ההחזרה של פונקציה פנימית קטנה | לא חובה |
| טיפוס ההחזרה של פונקציה מיוצאת או ציבורית | מומלץ, כחוזה |
| מערך או אובייקט ריק שממלאים מאוחר יותר | כן, const ids: number[] = [] |
| הערך צריך לשמור על הטיפוסים הליטרליים שלו | השתמשו ב-as const |
| הערך חייב להתאים לטיפוס אבל לשמור על הטיפוס שהוסק לו | השתמשו ב-satisfies |
הדרך המהירה ביותר לראות מה הוסק היא לרחף מעל השם בעורך. בלי עורך, השימו את הערך למשתנה מטיפוס שגוי, כמו const probe: boolean = mixed;, והודעת השגיאה של הקומפיילר תציין את הטיפוס שהוסק.
שאלות נפוצות
מה זה type inference ב-TypeScript?
זה הקומפיילר שמסיק טיפוס שלא כתבתם. let count = 0 נותן ל-count את הטיפוס number, ופונקציה שמחזירה a + b על שני מספרים מקבלת את טיפוס ההחזרה number. הטיפוס שהוסק נבדק בדיוק כמו טיפוס שנכתב.
צריך לכתוב טיפוס לכל משתנה ב-TypeScript?
לא. כתבו טיפוסים לפרמטרים של פונקציות (הם לא מוסקים מהקריאות, רק מערך ברירת מחדל או מהקשר של callback), לטיפוסי ההחזרה של פונקציות מיוצאות אם רוצים API יציב, ולמשתנים שמוצהרים בלי ערך. תנו ל-TypeScript להסיק את השאר: const name: string = "Ada" חוזר על מה שהקומפיילר כבר יודע.
למה const מסיק טיפוס ליטרלי אבל let מסיק string?
אי אפשר להשים מחדש ל-const, ולכן const dir = "left" מקבל את הטיפוס הליטרלי "left". let יכול להחזיק מאוחר יותר כל מחרוזת אחרת, ולכן let dir = "left" מורחב ל-string. אותה הרחבה קורית למאפיינים של אובייקט ליטרלי, כי אפשר להשים מחדש למאפיינים; as const עוצר אותה.
למה TypeScript אומר שלפרמטר יש במרומז את הטיפוס 'any'?
זו השגיאה TS7006 של noImplicitAny, שהיא חלק מ-strict. TypeScript לא מסיק טיפוסי פרמטרים מהאופן שבו קוראים לפונקציה, ולכן פרמטר בלי טיפוס יהיה any. הוסיפו הערת טיפוס, function double(x: number), או העבירו את הפונקציה למקום שבו הטיפוס שלה ידוע מההקשר, כמו callback ל-map.
איך רואים את הטיפוס ש-TypeScript הסיק?
רחפו עם העכבר מעל השם בעורך (VS Code ורוב העורכים מציגים את הטיפוס שהוסק בחלונית). בקוד, השמה של הערך למשתנה מטיפוס שגוי בעליל גורמת לקומפיילר להדפיס את הטיפוס שהוסק בהודעת השגיאה.