אינטרנט ישראל
  • ראשי
  • אודות רן בר-זיק ואינטרנט ישראל
  • ערוץ טלגרם
  • מסטודון
  • התחברו אלי בטוויטר
  • התחברו אלי בלינקדאין
  • ספר ג'אווהסקריפט
  • ראשי
  • אודות רן בר-זיק ואינטרנט ישראל
  • ערוץ טלגרם
  • מסטודון
  • התחברו אלי בטוויטר
  • התחברו אלי בלינקדאין
  • ספר ג'אווהסקריפט
ראשי » מדריכים » טייפסקריפט » טייפסקריפט: סוגים מתקדמים

טייפסקריפט: סוגים מתקדמים

רן בר-זיק מרץ 13, 2022 7:07 am 4 תגובות

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

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

Type Aliases

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

const squareSizeCalculator = (square: {x:number, y:number}):number => {
    return square.x*square.y;
}

let result = squareSizeCalculator({x:2,y:3});

console.log(result);

אבל אפשר לראות שזה טיפה בעייתי. למה? כי אם יש לנו אובייקט מורכב יחסית, אז אנחנו בבעיה:

const userOutput = (userObject: {firstName: string, lastName: string, id: string | number, city: string, zip: number, phone: string}) {
    // do something
}

גם מתישהו אנו נצטרך אולי למחזר את האובייקט הזה. אולי הוא מופיע בעוד מקומות. בדיוק בשביל זה אנחנו יכולים להגדיר סוג של אובייקט (type) באופן נפרד. איך? ממש כמו משתנה, רק במקום const או let (שאף אחד לא יזכיר פה את var!!!) יהיה לנו את המשתנה הזה, שנקרא aliases. שם נגדיר את כל מה שצריך ונוכל להשתמש ב-aliases הזה איפה שנרצה. הנה דוגמה להמחשה:

type user = {
    firstName: string,
    lastName: string,
    id: string | number,
    city: string,
    zip: number,
    phone: string
}

const userOutput = (userObject: user) => {
    // do something
}

userOutput({
    firstName: 'Ran',
    lastName: 'Bar-Zik',
    id: '6382020',
    city: 'Petah Tikva',
    zip: 6382020,
    phone: '03-6382020'
});

שימו לב ש-aliases זה לא ג'אווהסקריפט. בקובץ המקומפל לא תראו את הגדרת ה-type בכלל. זה פשוט טייפסקריפט שקובע איך אובייקטים נראים.

אפשר לקבוע type לא רק לאובייקטים אלא גם באופן כללי. למשל:

type ID = number | string;

אני אוכל להשתמש ב-ID הזה בכל פעם שיש לי number | string. אם אי פעם ארצה לשנות את זה, אני אוכל במקום אחד. הנה דוגמה לשימוש:

type ID = number | string;

type User = {
    firstName: string,
    lastName: string,
    id: ID,
    city: string,
    zip: number,
    phone: string
}

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

Interfaces

יחד עם alias שאותו אנו מכירים יש לנו גם interface. הוא עובד באופן די דומה ל-type. לא משתמשים ב-= אלא במילה השמורה interface. הנה, כך:

interface User {
    firstName: string,
    lastName: string,
    id: ID,
    city: string,
    zip: number,
    phone: string
}

const userOutput = (userObject: User) => {
    // do something
}

userOutput({
    firstName: 'Ran',
    lastName: 'Bar-Zik',
    id: '6382020',
    city: 'Petah Tikva',
    zip: 6382020,
    phone: '03-6382020'
});

ההבדל העיקרי בינהם הוא ש-interface אפשר להרחיב עם interface אחר באמצעות extends. באופן די קל ופשוט. הנה דוגמה שתבהיר את הכל:

type ID = number | string;

interface User {
    firstName: string,
    lastName: string,
    id: ID,
    city: string,
    zip: number,
    phone: string
}

interface PremiumUser extends User {
    subsriptionId: string,
    subscriptionStatus: string
  }

const userOutput = (userObject: PremiumUser) => {
    // do something
}

userOutput({
    firstName: 'Ran',
    lastName: 'Bar-Zik',
    id: '6382020',
    city: 'Petah Tikva',
    zip: 6382020,
    phone: '03-6382020',
    subsriptionId: '6382020',
    subscriptionStatus: 'gold'
});

במקרה הזה יצרתי אינטרפייס חדש שנקרא PremiumUser שפשוט הוא user עם עוד שתי שדות רלוונטיים לסוג הזה.

שימו לב: מקובל להשתמש באות גדולה בהגדרת type alias או interface.

Type Assertions

בפוסט על הגדרות משתנים, אמרנו ש"גם אם לא הגדרתם סוג, טייפסקריפט תגדיר את סוג המשתנה לפי ההשמה הראשונה. כך למשל, אם הגדרתי את someVar שיקבל ערך 5, מבלי להגדיר לו סוג, אז אם בהמשך אני אנסה להכניס אליו ערך לא מספרי, אני אקבל שגיאת טייפסקריפט." זה קורה כמובן גם בפלט של פונקציות מובנות בשפה. טייפסקריפט פשוט תנחש את סוג הפלט שיוצא ולפעמים זה פחות מתאים לנו. זו הסיבה שאנו יכולים ב-override. הוא נעשה עם as. למשל (דוגמה מתוך הדוקומנטציה). הפונקציה getElementById היא פונקציה טבעית בג'אווהסקריפט (מבוסס דפדפן) מחזירה לנו אלמנט DOM. אם נשתמש בה באופן הזה:

const myCanvas = document.getElementById("main_canvas");

אז טייפסקריפט תנסה לנחש את הסוג שחוזר, שזה אלמנט DOM אז מדובר באובייקט מסוג HTMLElement. אבל אם אנו יודעים שאנו קוראים לאובייקט HTMLElement מסוג קנבס, אנו יכולים לצמצם את הסוג ל: HTMLCanvasElement. אבל איך עושים את זה? מגדירים לטייפסקריפט שבמקרה הזה היא לא מנחשת. במקרה הזה אנחנו אומרים לה מה הסוג. איך? עם המילה השמורה as ואז שם הסוג:

const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;

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

const data: object = ['a', 'b', 'c'];

// data.length; // Error

console.log((data as string[]).length); // 3

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

דרך נוספת לבצע השמה של משתנה היא באמצעות סוגריים משולשים (עם חיצים) באופן הבא:

const myCanvas = <HTMLCanvasElement>document.getElementById("main_canvas");

או:

console.log((<Array<string>>data).length);

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

הגדרה של סוג קבוע

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

let a: 'world' = 'world';
a = 'hello'; // Type '"hello"' is not assignable to type '"world"'.

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

let a: 'small' | 'medium' | 'large';

a = 'small';
a = 'medium';
a = 'large';
a = 'big'; // Type '"bug"' is not assignable to type '"small" | "medium" | "large"'.

זה נחמד, לא?

הגדרה של ערך קבוע באובייקטים

לפעמים, בדיוק כמו במשתנים, אנו רוצים לקבע ערך קבוע בתכונה מסוימת באובייקט. למשל במקרה שבו יש לי אובייקט מסוג userObject ולו יש תכונה בשם userType. אני רוצה שלתכונה הזו יהיה אפשר לשים רק admin. מן הסתם, אני אבצע הגדרה של סוג קבוע כמו בסעיף הקודם. הנה דוגמה:

const userObject = { userType: 'admin' };

// Later on in the code

userObject.userType = 'user';

אבל שוד ושבר!נ זה לא יחזיר שגיאה. למרות שהגדרתי סוג קבוע. הפתרון? שימוש ב-as להגדרה קבועה. כך:

const userObject = { userType : 'admin' as 'admin'};

// Later on in the code

userObject.userType = 'user'; // Type '"user"' is not assignable to type '"admin"'.

אני יכול לעשות גם union פה כדי להגביל סוגים:

const userObject = { userType : 'admin' as 'admin' | 'user' as 'user'};

// Later on in the code

userObject.userType = 'something'; // Type '"something"' is not assignable to type '"user"'.

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

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

למאמר הקודם בסדרת המדריכים על טייפסקריפטעבודה עם TSC על פרויקט טייפסקריפט
למאמר הבא בסדרת המדריכים על טייפסקריפטטייפסקריפט: פונקציות מתקדמות
סדרת המדריכים על טייפסקריפט
Typescript

4 תגובות

  1. משתמש אנונימי (לא מזוהה) הגב מרץ 13, 2022 בשעה 1:16 pm

    אחלה מאמר, כתוב מעולה, עבודה יפה!

    חשוב לציין שהנקודה לגבי ההבדל שבין אינטרפייסים לטייפים שאי אפשר להרחיב טייפים הוא לא נכון. אפשר – טייפים משתמשים ב& במקום במילת הקוד extends.

    הנה חלק מהדוקומטנציה הרשמית שמסביר את ההבדלים:
    https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#differences-between-type-aliases-and-interfaces

  2. טיפוסית הגב מרץ 13, 2022 בשעה 6:56 pm

    תודה רבה על הפוסט 🙂
    אני חדשה יחסית ב- TS ורציתי לשאול – מתי נבחר להשתמש בinterface במקום בclass בts? זה כדי להימנע מהתעסקות עם ירושה?
    נתקלתי במצב בו רציתי לבדוק אם אובייקט הוא מטיפוס א׳ או ב׳, אבל בגלל ששני הטיפוסים היו interfaces לא יכלתי להשתמש בפתרון מהיר כמו אופרטור typeof.

  3. משתמש אנונימי (לא מזוהה) הגב מרץ 13, 2022 בשעה 8:56 pm

    יש עוד הבדל שהוא מהותי בין interfaces לtype aliases. שinterface הוא לא distrebuted. זה אומר שהכל מושטח (אין union למשל). מה שגם הופך אותו ליותר מהיר (interfaces מומלצים על ידי צוות הTS). בנוסף לא הייתי משתמש בas בשביל null assertion, אלא ב!. כי זה as מוריד משמעותית מהtype safety. ועוד דבר, as const הרבה יותר נחמד מאשר כל פעם לעשות copy & paste.

    וידאו טוב על distribution

    https://www.youtube.com/watch?v=IsnyTZi84ZY

  4. מישהו הגב מרץ 14, 2022 בשעה 10:36 am

    יפה יפה 🙂

    כמה הערות:

    1. כדאי להזכיר שאין אפשרות לעשות union בinterfaces (משהו כמו interface A {} | {}) וזה לפעמים מעצבן.
    2. אפשר לדמות extends בלי interfaces בעזרת intersection – כלומר type A = SuperType & {…}.
    3. הקטע הכי מגניב לconst strings זה כשזה מגיע עם interpolation – לדוג' אפשר לעשות:
    type Unit = "px" | "em" | …;
    type CSSValue = `${number}{Unit}`;
    ואז "123px" מתקבל אבל "123xp" או "12px3" לא (ההגדרה הזו לא מושלמת אבל אפשר להרחיב אותה, יש אנשים שכתבו דברים הזויים כמו JSON parsers עם זה, זה אפילו turing complete).
    4. במקום לחזור על הערך עם as "string", הדרך המקובלת היא לכתוב as const.
    5. אם אני זוכר נכון, ההמרה עם הסוגריים המשולשים לא עובדת עם jsx.

השארת תגובה

ביטול

ללמוד ג'אווהסקריפט בעברית

ללמוד לתכנת ג'אווהסקריפט בעברית שגייס יותר משלוש מאות אלף שקל ולמעלה מ-2000 תומכים - בואו ללמוד עכשיו איך לתכנת.

רשימת הנושאים
  • מדריכים
    • ריאקט
    • טייפסקריפט
    • ECMAScript 6
    • ES20XX
    • Node.js
    • Express
    • רספברי פיי
    • Babel
    • docker
    • MongoDB
    • Git
    • לימוד MySQL
    • SASS
    • jQuery
    • CSS3
    • HTML 5
    • SVN
    • LESS
  • פיתוח אינטרנט
    • פתרונות ומאמרים על פיתוח אינטרנט
    • jQuery Scripts
    • jQuery למתקדמים
    • יסודות בתכנות
    • נגישות אינטרנט
  • חדשות אינטרנט
  • מידע כללי על אינטרנט
    • רשת האינטרנט
    • בניית אתרי אינטרנט
  • rss logo

    לכל המאמרים

    לכל המאמרים שפורסמו באינטרנט ישראל משנת 2008 ועד עכשיו.
  • rss logo

    RSS Feed

    משתמשים בקורא RSS? אם כן, עקבו אחרי אינטרנט ישראל באמצעות פיד ה-RSS!
    מה זה RSS?
  • Twitter logo

    עקבו אחרי בטוויטר

    בחשבון הטוויטר שלי אני מפרסם עדכונים מהירים על חדשות בתחום התכנות והיזמות, התרעות על מצבי חירום ורכילות בוערת על תחום הווב.
    מה זה טוויטר?
  • facebook like image

    ערוץ הטלגרם של אינטרנט ישראל

    בערוץ הטלגרם של אינטרנט ישראל אני מפרסם את הפוסטים של באתר וכן עדכונים טכנולוגיים נוספים.
    מה זה טלגרם?
  • github logo

    הפרויקטים שלי בגיטהאב

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

כל הזכויות שמורות לרן בר-זיק ולאינטרנט ישראל | מדיניות הפרטיות של אתר אינטרנט ישראל | אתר אינטרנט ישראל נגיש לפי תקן WCAG 2.0 AA | הצהרת הנגישות של האתר | אבטחת מידע ודיווח על בעיית אבטחת מידע

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