בדיקת טיפוס בטייפסקריפט על הפונקציה הקולבק


התחלתי לאחרונה ללמוד TypeScript ונפגשתי בבעיה הזו שאני לא מבין מבחינה קונספטואלית. האם זה לא נכון להגדיר טיפוס פרמטר עבור הפונקציה הזו?

setTimeout((a: string) => {
    console.log(a); // ==> Prints 5 as a number
}, 1, 5);

למה לא מתבצעת בדיקת סוג כאשר קוראים לפונקציית החזרה?

איך אני יכול לגרום ל-TypeScript להוציא שגיאה?

מה הפתרונות שלי באופן כללי, כשנתקל במשהו כזה?

תשובה #1

זו בעיה ידועה ב-TypeScript, ראה microsoft/TypeScript#32186. ההגדרות עבור setTimeout() נראות כך

setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;

שמקבל כל רשימת ארגומנטים שהיא, מבלי להתעניין בסוגים. ו-TimerHandler מוגדר כסתם

type TimerHandler = string | Function

שמבצע שימוש בסוג Function שאינו בטוח וללא טיפוס. לכן setTimeout() לצערנו בעל טיפוס רופף מאוד ב-TypeScript. לעיתים במצבים כאלו אפשר להשתמש במיזוג הצהרות כדי להוסיף חתימות קריאה משופרות, אך במקרה זה שום דבר לא יעצור את הקומפיילר מלהתדרדר חזרה לחתימת הקריאה הלא בטוחה.

זו פשוט מגבלה ב-TS, לכאורה. הבעיה רשומה כ"Backlog", כך שהיא לא צפויה לקבל טיפול בזמן הקרוב. אתה יכול ללכת ל-microsoft/TypeScript#32186 לתת 👍 ולהראות תמיכה בתיקון שלה, אבל כנראה שזה לא ישתנה הרבה.

כרגע יש רק פתרונות עקיפים.


אתה יכול להחליט להשתמש בעותק המקומי שלך של ספריית lib.dom.ts, שם תשנה את ההגדרה של safeTimeout(), אבל זה יהיה מאוד מעצבן לתחזוקה.

פתרון חלופי שתוכלו להשתמש בו הוא לעטוף את setTimeout בתוך פונקציה משלכם שהיא יותר טיפוסית, ואז להשתמש רק בה. אולי משהו כזה:

const safeSetTimeout: <A extends any[]>(
    handler: (...args: A) => void, timeout?: number, ...args: A
) => number = setTimeout;

שזה משתמש בגנריים כדי להקל על סוגי הנתונים של args המצופים על ידי ה-handler callback להיות זהים לאלו שהועברו כ-args ל-setTimeout() עצמו. בואו נראה את זה עובד:

safeSetTimeout((a: string) => {
    console.log(a);
}, 1, 5); // <-- error!  number is not assignable to string
//    ~

מעולה, אנחנו מקבלים את שגיאת הקומפילר הצפויה על 5, שאומרת שהיא צריכה להיות string ולא number. זה הכי טוב שניתן להגיע אליו לעת עתה, למרבה הצער.

קישור למזחלת לקוד