טייפסקריפט: מבוא

מבוא לטייפסקריפט - למה צריך את זה? דוגמאות מעשיות ותחילת לימוד.
לוגו טייפסקריפט

אני מאמין שרוב הקוראים שמעו על טייפסקריפט, במאמר הזה ובמאמרים הבאים, שהקישורים אליהם יופיעו בתחתית המאמר הזה, אני אלמד טייפסקריפט מהבסיס, בעברית ובאופן פשוט. המטרה היא שסדרת המדריכים הזו תכנס, אחרי הפידבק שלכם ועיבוד נוסף, כנספח בספר שלי ללימוד ג'אווהסקריפט ואולי אחרי כן כפרק שלם. בסדרת המדריכים הזו התבססתי בעיקר על הדוקומנטציה של טייפסקריפט וה-Handbook המצוין שלהם באתר. בכל הנוגע לדוגמאות שימושיות ורעיון לסידור החומר נעזרתי גם באתר tutorialsTeacher.
למי שמעדיף לימוד עם סרטונים – החומר היחידי בעברית חוץ ממאמרים הוא סדרת הסרטונים של GoCode על טייפסקריפט (אני לא קשור ל-GoCode).

למי מיועד המדריך?

המדריך מיועד למתכנתים שמכירים ג'אווהסקריפט היטב. ברמה של עד תכנות אסינכרוני וכמובן ES6. בדיוק ברמה של הספר שלי "ללמוד ג'אווהסקריפט בעברית".

מה זה טייפסקריפט? למה צריך את זה?

טייפסקריפט (באנגלית Typescript) היא ג'אווהסקריפט עם סוגי מידע (באנגלית: Types). כלומר ג'אווהסקריפט שיש בה אכיפה של סוגי מידע. סוג מידע בג'אווהסקריפט בד"כ מקושר לסוג מידע פרימיטיבי. למשל בוליאני (True\False), מספר או מחרוזת טקסט. בג'אווהסקריפט אין אכיפה של סוגי מידע. מה זאת אומרת? אני אתן כדוגמה את הקוד הזה:

const weekDay = (val) => {
  switch (val) {
    case 1: 
      return 'Sunday';
      break;
    case 2: 
      return 'Monday';
      break;
    case 3: 
      return 'Tuesday';
      break;
    case 4: 
      return 'Wednesday';
      break;
    case 5: 
      return 'Thursday';
      break;
    case 6: 
      return 'Friday';
      break;
    default: 
      return 'Saturday';
  }
}

console.log( weekDay(1) ); // "Sunday"
console.log( weekDay('1') ); // "Saturday"

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

פתרון הגיוני לזה הוא לשים guards. כלומר משהו שבודק את הקלט כדי לוודא שזה באמת מה שאנו מצפים שיקרה. למשל:

  if (isNaN(val) || val > 7 || val < 0) {
    throw 'Invalid input';
  }
  val = parseInt(val);

וכמובן בדיקות יחידה שמנסות את הפונקציה בדרכים שונות, למשל זה:

describe('weekDay Function', function () {
  it('should return the day of the week when value is between 1 to 6', () => {
    const value = weekDay(1)
    expect(value).toBe('Sunday');
  });
  it('should return the day of the week when value is string between 1 to 6', () => {
    const value = weekDay('1')
    expect(value).toBe('Sunday');
  });
  it('should return Saturday of the week when value is between 7', () => {
    const value = weekDay(7)
    expect(value).toBe('Saturday');
  });
  it('should throw error when input is less than 1', () => {
    expect(() => {
      weekDay(0);
    }).toThrow();
  });
  it('should throw error when input is less more than 7', () => {
    expect(() => {
      weekDay(8);
    }).toThrow();
  });
  it('should throw error when input is not a number', () => {
    expect(() => {
      weekDay('a');
    }).toThrow();
  });
});

See the Pen Jest Example by Ran Bar-Zik (@barzik-the-vuer) on CodePen.

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

דוגמה נוספת לבעיה כזו, ואפילו קלאסית, היא במידע בוליאני. למשל:

const someBoolFunc = (value) => {
  if(value) {
    console.log('value is true')
  } else {
    console.log('value is false')
  }
}

someBoolFunc(true); // value is true
someBoolFunc(false); // value is false
someBoolFunc(); // value is false???

זה משהו קלאסי – אנו חושבים ומתכננים שכל ערך יהיה בוליאני. אבל אם ערך שהוא undefined מגיע, הוא false אוטומטית. אם הוא מגיע כ-0, כנ"ל. אבל 0 זה מספר! לא false! אז זהו. שג'אווהסקריפט מבצעת המרה אוטומטית של סוגי מידע פרימיטיביים. מספר לטקסט, טקסט לבוליאני, מספר לבוליאני וכך הלאה. בעוד שזו התנהגות שמתכנתי ג'אווהסקריפט מנוסים יודעים כבר לזכור, אלו שפחות נמצאים בבעיה.

אז הנה הבעיה, מה הפתרון?

בחברת מיקרוסופט היו לא מעט מהנדסים שנדרשו לתכנת בג'אווהסקריפט אחרי שהיו רגילים לשפות אחרות ומכך נוצר הצורך בטייפקסקריפט שמכניסה אכיפה של סוגים לתוך ג'אווהסקריפט. טייפסקריפט היא בקוד פתוח ומאוד פופולרית בגלל שלא מעט מתכנתים נתקלים בבעיה הזו של המרה. בעיה שטייפסקריפט מנסה לפתור. איך? אנו כותבים קוד ג'אווהסקריפט לכל דבר ועניין אבל בכל מקום שבו אנו צריכים אנו מגדירים את סוג המידע. בין אם מדובר בפונקציה או במקומות אחרים. אני רוצה בוליאני? אני אומר שאני רוצה בוליאני. רוצה מספר בין 1 ל-7? כנ"ל. תוכנת טייפסקריפט עוברת על הקוד שאנו כותבים, מוודאת שאין בו בעיות של מידע ואם הכל תקין היא ממירה אותו (תהליך ההמרה נקרא טרנספייל – מלשון Transpilation) לקוד ג'אווהסקריפט שהוא בעצמו רץ בסביבה של ג'אווהסקריפט, בין אם מדובר בדפדפן או בין אם מדובר בשרת.

איך זה עובד? בגדול אנו כותבים קוד טייפסקריפט ומציבים אותו בקובץ שהסיומת שלו היא ts, הטרנספיילר של טרנסקריפט רץ על הקבצים האלו וממיר אותם לג'אווהסקריפט כאשר אם יש שגיאות הוא מתריע. כאשר רוב הסביבות היום (למשל Next או create react app) באות אוטומטית עם טייפסקריפט כחלק מסביבת הפיתוח והבילד.

לשימוש מאוד ראשוני ולימודי, אנו נדגים עם Typescript playground של האתר הרשמי של טייפסקריפט. מדובר באתר שמבצע טרנספילציה לג'אווהסקריפט כמעט מייד. כשתכנסו לאתר תוכלו לראות מצד שמאל מקום שבו ניתן לכתוב בטייפסקריפט ומצד ימין את הפלט של ג'אווהסקריפט וכן את השגיאות שיש, אם יש.

ננסה להכניס את הקוד הבוליאני בדוגמה הקודמת, נראה שיש לנו שתי שגיאות:

Errors in code
Parameter 'value' implicitly has an 'any' type.
Expected 1 arguments, but got 0.

למה? כי ראשית לא הגדרנו בפונקציה את סוג המידע שהיא אמורה לקבל ושנית, באחת הקריאות לפונקציה (האחרונה), לא העברנו ארגומנט.

שגיאה בטרנספילציה

אפשר לראות שתהליך הטרינספול הצליח והקוד שלעיל הצליח להיות מתורגם לקוד ג'אווהסקריפט (הפתעה עצומה, זה קוד ג'אווהסקריפט נקי). אבל טייפסקריפט מתריעה על שתי בעיות.

הגדרה ראשונית של סוג מידע פרימיטיבי

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

const someBoolFunc = (value: Boolean) => {
  if(value) {
    console.log('value is true')
  } else {
    console.log('value is false')
  }
}

someBoolFunc(true); // value is true
someBoolFunc(false); // value is false

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

השלמת קוד בעורך הקוד של טייפסקריפט סנד בוקס

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

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

פוסטים נוספים שכדאי לקרוא

פתרונות ומאמרים על פיתוח אינטרנט

יישום של nonce על מנת להגן מפני התקפות injection

בפוסט הקודם הסברתי על hash עם CSP על משאבי inline – שזה נחמד ומעולה אבל פחות ישים בעולם האמיתי שבו בדרך כלל התוכן ה-inline (בין

גלילה לראש העמוד