• בלוג
  • החזרת מספר בטווח ב TypeScript

החזרת מספר בטווח ב TypeScript

02/05/2023

נתבונן בפונקציה הבאה בטייפסקריפט:

function createRandomNumber(): number {
    return Math.floor(Math.random() * 10);
}

הפונקציה מחזירה מספר אקראי בין 0 ל-9, אבל אנחנו לא יכולים לדעת את זה רק מקריאת הטיפוס שלה. אם נרצה לקחת את התוצאה של הפונקציה בתור אינדקס למערך קבוע, טייפסקריפט לא יוכל לוודא שבמערך יש 10 איברים.

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

דרך אחת טובה להוסיף את ה-10 לממשק היא פשוט לכתוב את כל המספרים השלמים מאפס עד תשע בתור ערך ההחזר של הפונקציה:

type Range10 = 0|1|2|3|4|5|6|7|8|9;
function createRandomNumber() {
    return Math.floor(Math.random() * 10) as Range10;
}

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

הפיתרון הגנרי משתמש ברקורסיה ונראה כך:

export type Range<N extends number,R extends number[]=[]> = 
    R['length'] extends N ?  R[number] : Range<N, [...R, R['length']]>;


function createRandomNumber() {
    return Math.floor(Math.random() * 10) as Range<10>;
}

וההסבר-

  1. הרקורסיה בונה את המערך R, כך שכל איטרציה היא מוסיפה ל R מספר נוסף.

  2. המספר שמתווסף הוא האורך הנוכחי של R, ולכן המספרים יהיו בטווח מאפס עד המספר N.

  3. בסוף הרקורסיה מחזירים איחוד של כל המספרים השמורים ב R.

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