טיפ טייפסקריפט: מחליפים if ב switch

04/10/2022

קוד הטייפסקריפט הבא לא מתקמפל, למרות שאין בו שום דבר לא בסדר:

type AB = "a"|"b";

function test(x: AB): number {
    if (x === "a") {
        return 1;
    } else if (x === "b") {
        return 2;
    }
}

זה הלינק לפלייגראונד אם אתם צריכים הוכחה: https://www.typescriptlang.org/play?#code/C4TwDgpgBAggQlAvFARAQxQHxQIxQbgChCAzAVwDsBjYASwHsKpgIBnYACgA8AuWOAJR8KZALY4IAJygBvQlAVRaJKNySJk6FANnzF+yRGBlJTAIxF9AXygQANq2jLVXdZrw65+g0ZNMATJaKVoRWQA

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

אנחנו יודעים שזה לא יכול לקרות כי בדקנו את כל האפשרויות של x, והיינו רוצים לשכנע בזה גם את טייפסקריפט. דרך אחת לעשות את זה היא לעבור ל switch:

function test(x: AB): number {
    switch(x) {
        case "a": return 1;
        case "b": return 2;
    }
}

עם זה אין בעיה כי טייפסקריפט מבין שה switch מכסה את כל המצבים. יותר מזה, אם בעתיד אני אשנה את הגדרת הטיפוס AB אז אקבל קו אדום בהגדרת הפונקציה ואדע שצריך לתקן אותה.

במשבצת התיקונים הפחות טובים אפשר למצוא את זה:

function test(x: AB): number {
    if (x === "a") {
        return 1;
    } else if (x === "b") {
        return 2;
    } else {
        // never happens
        return 0;
    }
}

שמתקן את הבעיה אבל לא מגן עליי משינוי עתידי ב AB.

וגם את זה שסובל מאותה בעיה:

// @ts-ignore
function test(x: AB): number {
    if (x === "a") {
        return 1;
    } else if (x === "b") {
        return 2;
    }
}

תיקון אחרון לפוסט שעובד הפעם אבל פחות מומלץ במקרה הכללי הוא:

function test(x: AB): number {
    return {
        "a": 1,
        "b": 2,
    }[x];
}

גירסה זו תזרוק שגיאה אם נשנה את ה Union, אבל החיסרון שלה הוא שהיא מחשבת את כל הערכים בלי קשר לערך של x. כאן זאת לא בעיה כי כולם קבועים אבל במקרה הכללי לא תמיד זה אפשרי או רצוי.