type assertion - מה המשמעות של "as const" ב-TypeScript ומה השימוש שלו?
אני מבולבל לגבי המרת as const. בדקתי כמה מסמכים וסרטונים אבל לא הבנתי את זה לגמרי.
הדאגה שלי היא מה משמעות as const בקוד למטה ומה היתרון בשימוש בו?
const args = [8, 5] as const;
const angle = Math.atan2(...args);
console.log(angle);
תשובה #1
זה ידוע כהצהרת const. הצהרת const אומרת למתורגמן להסיק את הסוג ההצדדי ביותר* או ההספציפי ביותר שהוא יכול עבור ביטוי מסוים. אם תוותר עליה, המתורגמן ישתמש בהתנהגות ברירת המחדל שלו להסקת סוגים, מה שעלול להוביל לסוג רחב יותר או כללי יותר.
שים לב שזה נקרא "הצהרה" ולא "המרה". המונח "המרה" בדרך כלל כדאי להימנע ממנו ב-TypeScript; כשהאנשים משתמשים ב"המרה" הם לעיתים מרמזים על סוג של אפקט שניתן להבחין בו בזמן ריצה, אבל מערכת הסוגים של TypeScript, כולל הצהרות סוגים והצהרות const, נמחקות לחלוטין מה-JavaScript שנוצר. לכן אין שום הבדל בזמן ריצה בין תוכנית שמשתמשת ב-as const לבין תוכנית שלא משתמשת.
עם זאת, בזמן הקומפילציה יש הבדל ניכר. בואו נראה מה קורה כשממשיכים בלי as const בדוגמה שלמעלה:
const args = [8, 5];
// const args: number[]
const angle = Math.atan2(...args); // error! Expected 2 arguments, but got 0 or more.
console.log(angle);
המחשב רואה const args=[8, 5]; ומסיק את סוג number[]. זהו מערך שניתן לשנותו עם אפס או יותר אלמנטים מהסוג number. המחשב אינו יודע כמה או אילו אלמנטים קיימים. מסקנה כזו היא בדרך כלל סבירה; לעיתים קרובות, תוכן המערך מיועד לשינוי כלשהו. אם מישהו רוצה לכתוב args.push(17) או args[0]++, הוא יהיה מרוצה מסוג number[].
לצערי, השורה הבאה, Math.atan2(...args), גורמת לשגיאה. הפונקציה Math.atan2() דורשת בדיוק שני ארגומנטים מספריים. אבל כל מה שהקומפיילר יודע על args הוא שמדובר במערך של מספרים. הוא שכח לחלוטין שיש שם שני אלמנטים, ולכן הקומפיילר מתלונן שאתה קורא ל-Math.atan2() עם "0 או יותר" ארגומנטים כאשר הוא רוצה בדיוק שניים.
השווה זאת לקוד עם as const:
const args = [8, 5] as const;
// const args: readonly [8, 5]
const angle = Math.atan2(...args); // okay
console.log(angle);
כעת המהדר מסיק ש-args הוא מהסוג readonly [8, 5]... טופל readonly שערכיו הם בדיוק המספרים 8 ו-5 בסדר הזה. באופן ספציפי, args.length ידוע על ידי המהדר שהוא בדיוק 2.
וזה מספיק כדי שהשורה הבאה עם Math.atan2() תפעל. הקומפיילר יודע ש-Math.atan2(...args) זהה ל-Math.atan2(8, 5), שהיא קריאה חוקית.
ושוב: בזמן הריצה, אין שום הבדל בכלל. שתי הגרסאות רושמות 1.0121970114513341 בקונסול. אך אבחנות const, כמו שאר מערכת הטיפוסים הסטטית, אינן מיועדות לגרום להשפעות בזמן הריצה. במקום זאת, הן מאפשרות לקומפיילר להבין יותר על כוונת הקוד, ויכולות להבדיל ביתר דיוק בין קוד תקין לבאגים.
קישור למזחוב קוד
* זה לא נכון בצורה מוחלטת עבור סוגי מערכים וטופלים; מערך או טופל readonly הוא טכנית רחב יותר מאשר גרסה שניתנת לשינוי. מערך שניתן לשנותו נחשב לתת-סוג של מערך readonly; הראשון אינו ידוע כבעל שיטות לשינוי כמו push() בעוד שלשני יש.
תשובה #2
בקצרה, זה מאפשר לך ליצור אובייקטים לקריאה בלבד, זה ידוע כ-const assertion, בקוד שלך as const משמעותו שערכי המיקומים במערך הם readonly, הנה דוגמה כיצד זה עובד:
const args = [8, 5] as const;
args[0] = 3; // throws "Cannot assign to '0' because it is a read-only
args.push(3); // throws "Property 'push' does not exist on type 'readonly [8, 5]'"
אתה יכול לראות בשגיאה שהוחרגה לאחרונה, כי args=[8, 5] as const מפורש כ-args: readonly [8, 5], זאת בגלל שההצהרה הראשונה שקולה לטופל בלתי ניתן לשינוי.
לטופלים יש הרבה שימושים, כמה דוגמאות הן: פרמטרים מתפשטים, ערכים הרסניים, וכו'.. תועלת כללית היא ההתנהגות של readonly שנוספת לכל מאפייני האובייקט שלהם:
const args = [8, 5];
// Without `as const` assert; `args` stills a constant, but you can modify its attributes
args[0] = 3; // -- WORKS ✓
args.push(3); // -- WORKS ✓
// You are only prevented from assigning values directly to your variable
args = 7; // -- THROWS ERROR ✖
ישנם כמה חריגים לגבי ה־asserts שהן 'לקריאה בלבד', אתה יכול לבדוק אותם כאן. למידע נוסף, הנה רשימה של שאלות/תשובות נוספות שעמדו לי להבין את ה־const assertion:
- איך אני מצהיר על מערך קבוע לקריאה בלבד בטייפסקריפט?
- האם יש הבדל בין הטענות סוג לבין אופרטור ה-
asהחדש ב-TypeScript? - טופלים לקריאה בלבד ב-TypeScript
תשובה #3
אם היית כותב const args=[8, 5], שום דבר לא ימנע ממך לכתוב גם args[0]=23 או args.push(30) או כל דבר אחר כדי לשנות את המערך הזה. כל מה שעשית הוא לומר ל-TS/JS שהמשתנה בשם args מצביע על המערך הספציפי הזה, ולכן אינך יכול לשנות למה הוא מפנה (למשל, אינך יכול לעשות args="משהו אחר"). אתה יכול לשנות את המערך, פשוט אינך יכול לשנות למה שהמשתנה שלו מפנה.
מצד שני, הוספת as const להצהרה עכשיו באמת הופכת אותה לקבועה. כל הדבר קריא בלבד, כך שאין אפשרות לשנות את המערך בכלל.
כדי להבהיר, כפי שהוצע בהערות:
״באמת עושה את זה קבוע״ יכול לרמוז שיש איזה אפקט בזמן ריצה כאשר אין כזה. בזמן ריצה, args.push(30) עדיין ישנה את המערך. כל מה ש-as const עושה הוא לגרום למתרגם TypeScript להתלונן אם הוא רואה שאתה עושה זאת. - jcalz
as const משפיע רק על המהדר, ויש יוצא מן הכלל להשפעתו הקריאה-בלבד (ראו את ההערות). אבל באופן כללי, זו עדיין ההבדל העיקרי בשימוש בין const ל-as const. האחד משמש ליצור הפניה בלתי משתנה והשני משמש ליצור את הדבר אליו מפנים בלתי משתנה.
תשובה #4
`as const` כאשר הוא מוחל על אובייקט או מערך, הוא הופך אותם לבלתי ניתנים לשינוי (כלומר, לקריאה בלבד). עבור ליטרלים אחרים, הוא מונע הרחבת סוג.
const args = [8, 5] as const;
args[0] = 10; ❌ Cannot assign to '0' because it is a read-only property.
כמה יתרונות נוספים :
- אתה יכול לתפוס באגים בזמן הקומפילציה מבלי להריץ את התוכנית אם לא תבצע המרה לסוג אחר.
- המקומל לא יאפשר לך להקצות מחדש תכונות של אובייקטים מקוננים.
תשובה #5
זו היא הצהרת const. הנה פוסט מועיל עליהם, וכאן התיעוד.
כאשר אנו בונים ביטויים מילוליים חדשים עם הצהרות const, אנו יכולים להעביר לשפה את ההודעה ש
- אין להרחיב סוגים מילוליים בהבעה הזו (למשל, אין לעבור מ"hello" למחרוזת)
- ליטרלים של אובייקטים מקבלים תכונות לקריאה בלבד
- מילויי מערך הופכים לצמדים לקריאה בלבד
עם const args=[8, 5] as const;, הסעיף השלישי חל, ו-tsc יבין זאת כך:
// Type: readonly [8, 5]
const args = [8, 5] as const;
// Ok
args[0];
args[1];
// Error: Tuple type 'readonly [8, 5]' of length '2' has no element at index '2'.
args[2];
בלי ההצהרה:
// Type: number[]
const args = [8, 5];
// Ok
args[0];
args[1];
// Also Ok.
args[2];
