לחשוב ב- TypeScript

23 בדצמבר 2013

תגיות: , ,
3 תגובות

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

הבעיה – קידוד אפליקציות web גדולות

לכתוב אפליקציות web זה לא מה שהיה פעם. האינטרנט הפך מאוסף של דפי HTML בדידים מלאים בתוכן סטטי לאוסף אפליקציות גדולות ואינטראקטיביות. משתמשים בימינו מצפים לחווית שימוש דמויית desktop – ללא ריענון של דפים, גרפיקה ואנימציה באיכות גבוהה ו- layout רספונסיבי. רק לפני מספר שנים יכולנו לכתוב את אתר האינטרנט המתקדם ביותר בסביבה באמצעות טכנולוגית ה- server המועדפת עלינו (לדוגמא ASP.NET MVC לכל אוהדי Microsoft מביניכם) ולתבל את צד ה- client במעט JavaScript. היום, פיתוח של אפליקציות single-page דורש כמויות עצומות של קוד client. מספר טכנולוגיות ניסו למלא את הצורך הזה – ביניהן Flash ו- Silverlight – אבל אלו אינן טכנולוגיות פתוחות ודורשות התקנת plugin-ים לדפדפן כדי לפעול. כששקע האבק, טכונולוגיות ה-web הסטנדרטיות HTML/CSS ניצחו. ומהי שפת התיכנות בה עלינו להשתמש כדי לכתוב אפליקציות כאלו? JavaScript כמובן. וכך היה שמכמות קטנה של קוד שהיינו צריכים לכתוב, לדבג ולתחזק, אנו מוצאים את עצמנו עם codebase ענקי של JavaScript ושם טמונה הבעיה.

אישית איני מחבב JavaScript – אני מוצא כי זו שפה שלא תוכננה היטב (אם אפשר לומר שהיא "תוכננה" בכלל…). היא דינמית לחלוטין ומורצת ע"י interpreter כך שאנו לא מנהנים מ- compiler או type safety בסיסי. היא מלאה בפיצ'רים מבלבלים כמו המרת טיפוסים אוטומטית וקריאה לפונקציות עם מספר ארגומנטים שגוי שה- interpreter סובל בשקט. ל- JavaScript חסרים מושגים מעולם התיכנות מונחה עצמים שכולנו מכירים ואוהבים משפות כגון C++, C# ו-Java – מושגים כגון classes, interfaces, properties ורבים אחרים.

כל עוד כמות קוד ה- JavaScript היא קטנה וצוות הפיתוח שלכם קטן מאד, יתכן שתצליחו להתמודד. אך אם אתם מפתחים אפליקציות web גדולות ומורכבות עם המון קוד וצוות פיתוח גדול ודינמי, מהר מאד תמצאו את עצמכם בגהינום JavaScript. אני יודע זאת מניסיון אישי, מפיתוח אפליקציה כזו בשנה האחרונה. לאחר חודשים ספורים, מצאנו את עצמנו לא מסוגלים לבצע שום refactoring משמעותי – לא ידענו מתי פיסת קוד כלשהי תישבר כתוצאה מכך. שיכפולי קוד החלו לצוץ מאחר והמפתחים החליטו ששיכפול הקוד ושינויו לצרכיהם הוא הרבה פחות מסוכן מביצוע refactoring ושימוש חוזר. מצב קוד ה- client-side שלנו הפך לבלתי נסבל.

דרכים להתמודד עם הבעיה

אז כיצד נוכל להתמודד עם JavaScript codebase גדול ולשרוד? ישנן 2 תשובות שאני שומע לעיתים תכופות:

1. להשתמש בקונבנציות ולכתוב הערות ודוקומנטציה לקוד. יש לוודא כי כל חבר בצוות הפיתוח כותב קוד בצורה דומה: אותו מבנה ספריות וקבצים, אותם naming conventions, אותם design patterns. יש לתעד את הקוד ולידע מפתחים אחרים על קוד חדש ובעל ערך שימוש חוזר.

2. לכתוב unit tests – לגבות כל שורה בקוד בטסטים שבודקים אותה ולהריץ אותם כל הזמן. כך אפשר לבצע refactoring ואם משהו נשבר, נדע על כך מיד שכן הטסטים שלנו לא יעברו.

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

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

TypeScript והישועה המיוחלת

כאן נכנסים Microsoft ו- TypeScript לתמונה. מהי בעצם TypeScript? בקצרה, זוהי קבוצת-על (superset) של שפת JavaScript המוסיפה לשפה פיצ'רים רבים וטובים הכוללים:

1. מושגים מעולם התכנות מונחה עצמים – classes, interfaces, constructors, תורשה, static members ו- access modifiers (private, public).

2. Type Annotations – עבור טיפוסי הנתונים הבסיסיים של JavaScript וגם עבור הטיפוסים שאתם מגדירים.

3. מודולים – מושג אנלוגי במקצת ל- namespaces ב- .NET (אם כי לא לגמרי), המאפשר לכם לסדר את הקוד שלכם באיזורים נפרדים ולהחליט מה לחשוף החוצה.

וחשוב מכל, TypeScript מגיע עם קומפילר שהופך TypeScript ל- JavaScript פשוט ותקני, אבל בדרך מבצע את כל הבדיקות שאנו מצפים מקומפילר לבצע – הוא בודק שכל המזהים בקוד הוכרזו, שלהשמות (assignments) יש טיפוסים תואמים בשני הצדדים, שפונקציות נקראות עם מספר ארגומנטים מתאים, שמשתמשים ב-members של טיפוסים לפי ה- access modifiers שלהם ועוד. רבות מבדיקות אלו מתבצעות בזמן קומפילציה בלבד – הן לא יתבצעו בזמן ריצה. לדוגמא, ה- private access modifier נעלם בקומפלציה ולא נבדק בזמן ריצה שכן אינו חלק משפת JavaScript.

כדי להמחיש בקצרה פיצ'רים אלו וכיצד TypeScript מתקמפל ל- JavaScript, הנה דוגמא קצרה:

// Interface

interface IShape {

    getArea(): number;

}

// Module

module Shapes {

    // Class

    export class Rectangle implements IShape {

        public color: string;

        // Constructor

        constructor(public width: number, public height: number) {

        }

        // Instance member

        getArea() { return 2 * this.width + 2 * this.height; }

    }

}

והנה ה- JavaScript המקומפל:

// Module

var Shapes;

(function (Shapes) {

    // Class

    var Rectangle = (function () {

        // Constructor

        function Rectangle(width, height) {

            this.width = width;

            this.height = height;

        }

        // Instance member

        Rectangle.prototype.getArea = function () {

            return 2 * this.width + 2 * this.height;

        };

        return Rectangle;

    })();

    Shapes.Rectangle = Rectangle;

})(Shapes || (Shapes = {}));

כמה דברים ששווה לשים לב אליהם:

1. ראו כמה יותר מצליח להביע ה- TypeScript ביחס ל- JavaScript שנוצר. אנחנו רואים שהמפתח הגדיר טיפוס חדש (Rectangle), שיש לו interface IShape המאפשר לנו ליצור טיפוסים אחרים המממשים אותו, שיש ל- Rectangle שני שדות מטיפוס number וניתן לגשת אליהם מחוץ לטיפוס עצמו (public). כלהאינפורמציה הזו נעלמת כשעוברים לגירסת ה- JavaScript.

2. הכרזה של שדה ללא השמה לתוכו (color) אינה מופיעה בקוד ה- JavaScript כי אין לפעולה זו מקבילה שם. הקומפילר משתמש בהכרזה זו כדי לוודא כי כל השמה עתידית לאובייקט מסוג זה תוכל להתבצע רק לשדות שהוגדרו עליו.

3. ראו כיצד ה –constructor מתורגם לפונקציית JavaScript רגילה.

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

var rect: IShape = new Shapes.Rectangle(3, 4); // Create a rectangle instance and assigns it to an IShape reference

var area = rect.getArea(); // OK, area = 14

var area = rect.getArea(5); // Compilation Error - getArea() has zero arguments

var color = rect.color; // Compilation Error - color is not part of the IShape interface

var color = (<Shapes.Rectangle> rect).color; // OK - downcast to Rectangle, can access color now

עוברים ל- TypeScript

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

התשובה הפשוטה היא שאין מה לדאוג. ראשית, השפה מבוססת על syntax של Ecmascript 5 (ES5) ומכילה מספר פיצ'רים מוצעים של ES6. עם התקדמות הסטנדרט, TypeScript תשתנה כדי להתאים עצמה אליו. לכן הטכנולוגיה אינה באמת proprietary, היא ממומשת בקוד פתוח ומבוססת על סטנדרטים פתוחים. שנית וחשוב יותר, זכרו כי הקומפילר מייצר קוד JavaScript פשוט ותיקני כך שגם אם TypeScript יכחד מן העולם (זה תמיד יכול לקרות), תוכלו פשוט למחוק את קבצי ה- TypeScript מפרוייקט ה-web שלכם ולהשאר עם אפליקציה עובדת ועם קוד ה- JavaScript הכי נקי שאי פעם תוכלו לכתוב. אין בשימוש ב- TypeScript שום סיכון.

יתכן כי אתם גם חושבים לעצמכם "יש לי המון קוד JavaScript קיים. אני לי פנאי לעצור הכל ולהעביר את הכל ל- TypeScript". ובכן, אינכם חייבים. TypeScript הוא קבוצת-על של JavaScript. במילים אחרות, פיסת קוד JavaScript היא כבר פיסת קוד TypeScript תקפה לגמרי. לכן ביצוע מיגרציה ל- TypeScript כרוכה בשינוי סיומות קבצי ה-JS שלכם לסיומת TS. כבר ע"י ביצוע פעולה פשוטה זו, הרווחתם מספר בדיקות שימושיות של הקומפילר כגון בדיקה שכל המזהים הוכרזו ושפונקציות נקראות עם מספר ארגומנטים נכון.

"ומה לגבי ספריות צד שלישי? האם אוכל להינות מ- TypeScript גם שם?" התשובה לכך היא בהחלט כן. TypeScript תומך "בקבצי הכרזה" (declaration files), קבצים שאינם מכילים קוד לביצוע אלא רק מספקים interfaces ו- type annotations לספריות קיימות. קהילת הפיתוח כבר יצרה קבצים כאלו כמעט לכל ספריית JavaScript שיש להעלות על הדעת (jQuery, AngularJS, KnockoutJS ורבות אחרות). באתר הזה תוכלו למצוא את רשימת הקבצים הקיימים.

שימוש ב- TypeScript בתוך Visual Studio

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

כדי להתחיל להשתמש ב- TypeScript, התקינו את TypeScript plugin for VS 2012 and 2013. אם אתם משתמשים ב- VS 2012, אני ממליץ להתקין גם את Web Essentials 2.9 כדי לקבל כלים נוספים כגון עורך TypeScript עם מסך מפוצל המראה גם את ה- JavaScript המקומפל ופונקציונליות של קימפול אוטומטי בשמירת הקובץ. כמו-כן, ReSharper 8.1 צפוי לתמוך ב- TypeScript. גירסאת הבטא זמינה כעת ותוכלו לנסות אותה.

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

1. הפיצ'ר של "קומפילציה בשמירת קובץ" אינו מושלם (בלשון המעטה). לא כל הקבצים בפרוייקט מתקמפלים יחד תמיד וחלק מהשגיאות מופיעות רק כאשר פותחים קובץ לעריכה.

2. צוות Web Essentials הסיר את התמיכה ב- TypeScript החל מגירסא 3.0 ובכל גירסאות VS 2013 מסיבה כלשהיא. זהו התירוץ שלהם. כדאי להשאר עם גירסא 2.9 אם לא תרצו לוותר על כלי ה- TypeScript.

3. התמיכה ב- TypeScript ב-VS 2013 הלכה מספר צעדים לאחור. עובדה זו הייתה מתסכלת מאד עבורנו ואילצה אותנו לחזור לפתח ב-VS 2012. ב-2013 קבצי ה-JS המקומפלים אינם חלק מהפרוייקט ויש להוסיפם באופן ידני. גרוע מכך, פיצ'ר "הקומפילציה בשמירה" נשבר לגמרי ואי-אפשר להשתמש בו בצורה מהימנה. וכמובן, אין תמיכה של web Essentials. בתקווה באגים אלו יתוקנו בגירסא 1.0 של TypeScript.

לחשוב ב- TypeScript

בעוד שכל היתרונות של TypeScript שצויינו לעיל הינם בעיקר טכניים, היתרון הגדול עבורי הוא שהמעבר ל- TypeScript שינה את הדרך בה אני חושב כשאני מקודד ב- JavaScript. עבורנו המפתחים הרגילים לשפות מונחות עצמים, אנו כותבים קוד תוך שימוש בדפוסים מסויימים – encapsulation, תורשה, עקרונות SOLID ועוד design patterns רבים. זו הדרך הטבעית לחשוב בה כשמשתמשים ב- C# או Java כי מושגים אלו בנויים לתוך השפה. כשעברתי ל- JavaScript (לפחות עבורי) מושגים אלו נעלמו כלא היו והתחלתי לחשוב במושגים של אובייקטים, פונקציות ותו לא. ישנן דרכים לחקות דפוסים של תיכנות מונחה עצמים ב- JavaScript באמצעות שימוש באובייקטים, פונקציות וטריקים מתוחכמים עם closures, אבל דרכים אלו מרגישות לא טבעיות ודורשות מיומנות גבוהה ומשמעת כדי לממשן. TypeScript הופך את JavaScript לשפה מונחית עצמים מהשורה הראשונה ומאפשרת לנו להמשיך לחשוב במושגים ודפוסים טובים ומוכרים.

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

Yaniv_YosifovichCodeValueפוסט נכתב ע”י  יניב יוסיפוביץ’, ארכיטקט תוכנה בכיר ויועץ בחברת CodeValue. ליניב כ-10 שנות ניסיון בטכנולוגיות Microsoft שונות כגון WPF, Azure, ASP.NET ואחרות. יניב מתכנן ומפתח אפליקציות web, enterprise ו- cloud במתודולוגיות Scrum ובעל תואר בהנדסת תוכנה מאוניברסיטת תל-אביב.

חברת CodeValue מובילה בשירותי תוכנה וביצוע פרויקטים, באמצעות בניית הגשר בין טכנולוגיות חדשניות וצרכים עסקיים ספציפיים, תוך הענקת חוויית משתמש ברמה הגבוהה ביותר. חברת CodeValue מבצעת בהצלחה פרויקטיי פיתוח תוכנה במגוון פלטפורמות ומספקת ייעוץ תוכנה ופיתוח המותאם לצרכי הלקוח.. חברת CodeValue מתמקדת במספר נושאים מרכזיים בעולם התוכנה, ביניהם ALM ו DevOps, מחשוב ענן, עולם ההתקנים הניידים, מענה אחוד וכולל לשווקי ה-UI/UX, פיתוח מערכות מידע ועוד. החברה מונה כיום כ-80 עובדים בהם מומחי טכנולוגיה בעלי ניסיון רב, הנחשבים מובילים בתחומם ומוכרים כסמכות מקצועית.

הוסף תגובה
facebook linkedin twitter email

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *

3 תגובות

    1. שלומי אסף15 במרץ 2014 ב 1:07

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

      הרעיון מאחורי TYPESCRIPT הוא לשמור על תכנות קרוב למקור ככל שאפשר תוך בצוע התאמות לעולם ה-OOP.
      העובדה שהולכים יד ביד עם ES6 תאפשר תאורטית לעבור לנתיב תכנות JS בהמשך בצורה שקופה ללא התאמות.
      תכנות ב-TS מאוד דומה ל-JS ובעצם אפשר לכתוב קוד JS בתוך TS, זה מאפשר למתכנת להשאר UP TO DATE, מעודכן ובקיא ב-JS.

      לדעתי בטווח הארוך TS משתלם יותר.

      הגב
  1. שלומי אסף15 במרץ 2014 ב 1:03

    יניב,
    קובץ ה-JS לא אמור בכלל להיות בפרוייקט.
    נכון שבגרסאות קודמות אכן קובץ ה-JS היה כחלק מהפרוייקט והיה בעצם "בן" של קובץ הטייפסקריפט (כמו גם MAP) אבל זה כבר לא קיים.

    מבחינת מיקרוסופט (ובצדק) קובץ ה-JS הוא קובץ תוצר וככזה לא אמור להיות בפרוייקט בדיוק כשם שקובץ DLL של CLASS LIB לא מצורף לפרוייקט לאחר קימפול.

    יתרה מזאת, צירוף קובץ JS לפרוייקט מקשה על עבודה ביחד עם TFS, זה פשוט בלתי אפשרי. כשאתה שומר קובץ TYPESCRIPT מתבצע ברקע "קימפול" ל-JS, תחת TFS כאשר קובץ ה-JS מצורף לפרוייקט הרי שהוא במצב READ ONLY בשביל שה-TFS יוכל לנהל אותו.
    באותו הרגע תהיה שגיאה מכיוון שהקימפול לא יצליח כי לא ניתן לכתוב לקובץ ה-JS.

    אין צורך להוסיף את קבצי ה-JS לפרוייקט, כל פעם שעושים BUILD מתבצעת בניה שלהם… (ההתקנה של TS דואגת לזה).

    הגב