Menu

TypeScript tuple: תחביר, איברים עם שמות, אופציונליים ו-rest

Tuple ב-TypeScript הוא מערך עם מספר קבוע של איברים שהטיפוסים שלהם ידועים לפי מיקום, כמו [string, number]. למדו את התחביר, איברים עם שמות, איברים אופציונליים ו-rest, tuples לקריאה בלבד ו-as const, החזרת tuples מפונקציות, ובמה tuples שונים ממערכים.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

Tuple ב-TypeScript הוא מערך עם מספר קבוע של איברים, שבו לכל מיקום יש טיפוס משלו. [string, number] פירושו בדיוק שני איברים: קודם מחרוזת, אחר כך מספר. כותבים את הטיפוסים בסוגריים מרובעים, בסדר שבו הערכים מופיעים.

בזמן ריצה tuple הוא מערך JavaScript רגיל. כל מה ש-tuple מוסיף (האורך הקבוע והטיפוס בכל מיקום) נבדק על ידי הקומפיילר ונמחק.

תחביר של tuple

טיפוס tupleמקבלטיפוס length
[string, number]בדיוק מחרוזת, ואחריה מספר2
[x: number, y: number]אותו דבר, עם תוויות לקריאות2
[number, number, number?]2 או 3 מספרים2 | 3
[string, ...number[]]מחרוזת, ואחריה מספר כלשהו של מספריםnumber
[...string[], number]מספר כלשהו של מחרוזות, ואחריהן מספרnumber
readonly [number, number]זוג שאי אפשר לשנות2
[]רק מערך ריק0

כל צורה מוסברת בהמשך. שימו לב לטיפוס של length: ב-tuple קבוע הוא טיפוס ליטרלי, כך שהקומפיילר יודע ש-pair.length הוא בדיוק 2.

מה הקומפיילר בודק

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

index.ts(2,7): error TS2322: Type '[string]' is not assignable to type '[string, number]'.
  Source has 1 element(s) but target requires 2.
index.ts(3,36): error TS2322: Type 'number' is not assignable to type 'string'.
index.ts(3,40): error TS2322: Type 'string' is not assignable to type 'number'.
index.ts(5,16): error TS2493: Tuple type '[string, number]' of length '2' has no element at index '2'.

מערך רגיל לא היה יכול לתפוס את השגיאה האחרונה: עבור string[], arr[2] הוא פשוט string שבמקרה הוא undefined בזמן ריצה.

איברי tuple עם שמות

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

מאז TypeScript 5.2 אפשר לתת תוויות לחלק מהמיקומים ולהשאיר אחרים בלי תווית, כמו ב-[first: string, number]. התוויות נועדו לקוראים בלבד: [x: number, y: number] ו-[number, number] הם אותו טיפוס, וניתנים להשמה זה לזה.

איברים אופציונליים

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

קריאה של איבר אופציונלי נותנת T | undefined, ולכן צריך ערך ברירת מחדל בתבנית ה-destructuring (a = 1) או בדיקה לפני חישוב.

איברי rest

איבר rest, ...T[], מייצג מספר כלשהו של איברים מטיפוס T. הוא יכול להופיע בסוף, בהתחלה או באמצע, ולכל היותר אחד בכל tuple.

ה-length של tuple עם איבר rest הוא number, כי הגודל כבר לא קבוע. מה שנשאר קבוע הוא המקום של המיקומים עם הטיפוסים.

Tuples לקריאה בלבד ו-as const

readonly [T, U] מסיר את push, pop, splice ואת ההשמה לפי אינדקס, וזה בדיוק מה שערך באורך קבוע צריך להיות. כתיבת as const אחרי מערך ליטרלי מסיקה tuple לקריאה בלבד של טיפוסים ליטרליים.

(typeof SIZES)[number] הופך את ה-tuple ל-union של טיפוסי האיברים שלו, דפוס שמוסבר בעמוד indexed access types. אי אפשר להעביר tuple לקריאה בלבד לפרמטר שמוגדר כ-tuple שניתן לשינוי, ולכן פונקציות שרק קוראות צריכות לקבל readonly [number, number].

הבדיקה של readonly קיימת רק בזמן קומפילציה. בזמן ריצה המערך לא מוקפא (ההשמה שלמעלה באמת רצה, כמו שהפלט מראה), ולכן השתמשו ב-Object.freeze אם צריך הבטחה בזמן ריצה.

החזרת tuple מפונקציה

החזרה של כמה ערכים כ-tuple היא הדרך שבה useState של React עובד (const [value, setValue] = useState(0)). המלכודת: מערך ליטרלי ב-return מוסק כמערך, לא כ-tuple.

index.ts(9,13): error TS2365: Operator '+' cannot be applied to types 'number | (() => number)' and 'number'.
index.ts(10,1): error TS2349: This expression is not callable.
  Not all constituents of type 'number | (() => number)' are callable.
    Type 'number' has no call signatures.

הפונקציה מחזירה (number | (() => number))[], ולכן שני השמות שחולצו ב-destructuring מקבלים את טיפוס ה-union. יש שני תיקונים: להצהיר על טיפוס ההחזרה, או להוסיף as const.

ערך החזרה מסוג tuple מאפשר למי שקורא לפונקציה לקרוא לחלקים איך שהוא רוצה. כשיש יותר משניים או שלושה ערכים, או כשהסדר לא ברור מאליו, החזירו אובייקט במקום: { count, increment } מתעד את עצמו.

Tuples כפרמטרים של פונקציה

פרמטר rest שמוגדר כ-tuple מתאר רשימת ארגומנטים שלמה, כולל ארגומנטים אופציונליים. כך טיפוס העזר המובנה Parameters<T> מייצג את הפרמטרים של פונקציה.

פריסה של tuple לתוך קריאה בודקת כל ארגומנט לפי המיקום שלו, משהו שפריסה של (string | number)[] לא יכולה לעשות.

Tuple מול מערך

מערך (string | number)[]Tuple [string, number]
אורךכלשהוקבוע (או מוגבל על ידי איברים אופציונליים ואיברי rest)
הטיפוס של x[0]string | numberstring
הטיפוס של x[5]string | numberשגיאת קומפילציה TS2493
הטיפוס של lengthnumber2
סדר הטיפוסיםלא נשמרנשמר
הערך בזמן ריצהמערך JavaScriptאותו מערך JavaScript
שימוש נפוץרשימות של פריטים דומיםקבוצות קטנות וקבועות: זוגות, קואורדינטות, [key, value], כמה ערכי החזרה

Tuples מופיעים גם בטיפוסים מובנים. Object.entries(obj) מחזיר [string, T][], ו-Map נבנה מ-tuples של [key, value]:

מלכודת אחת: ל-tuple שניתן לשינוי עדיין יש כל מתודות המערך, ולכן pair.push(3) מתקמפל על [string, number] ויוצר בשקט מערך של שלושה איברים שהטיפוס שלו אומר שניים. הצהרה על tuples כ-readonly סוגרת את הפרצה הזו. ומאחר שהטיפוסים נמחקים, נתונים מחוץ לתוכנית (JSON, API) לא נבדקים מול טיפוס tuple בזמן ריצה: בדקו את האורך ואת טיפוסי האיברים לפני שסומכים עליהם.

Variadic tuple types

טיפוסי tuple יכולים לפרוס טיפוסי tuple אחרים, [...T, ...U]. בשילוב עם generics, זה נותן טיפוסים לפונקציות שמשרשרות או מוסיפות איברים בהתחלה ושומרות על כל מיקום:

גם טיפוסים של ספריות נשענים על הסקת tuples: Promise.all([fetchUser(), fetchPosts()]) נפתר ל-tuple עם טיפוס אחד לכל promise בקלט.

שאלות נפוצות

מה זה tuple ב-TypeScript?

Tuple הוא טיפוס מערך באורך קבוע שבו לכל מיקום יש טיפוס משלו: [string, number] הוא בדיוק שני איברים, מחרוזת ואחריה מספר. בזמן ריצה זה מערך JavaScript רגיל; האורך והטיפוסים לפי מיקום נבדקים רק בזמן קומפילציה.

מה ההבדל בין tuple למערך ב-TypeScript?

לטיפוס מערך כמו (string | number)[] יש אורך כלשהו ולכל האיברים יש אותו טיפוס (union), ולכן arr[0] הוא string | number. ל-tuple כמו [string, number] יש אורך ידוע, t[0] הוא string, t[1] הוא number, ו-t[2] הוא שגיאת קומפילציה.

איך מחזירים tuple מפונקציה ב-TypeScript?

הצהירו על טיפוס ההחזרה, function f(): [number, string], או סיימו את ביטוי ההחזרה ב-as const, שנותן tuple לקריאה בלבד. בלי אחד מהשניים, return [count, setCount] מוסק כמערך של union, כמו (number | (() => void))[], ו-destructuring נותן טיפוסי union.

מה הם איברי tuple עם שמות?

תוויות על המיקומים, [name: string, age: number]. הן לא משנות את הטיפוס או את אופן הגישה (עדיין t[0]), אבל עורכי קוד מציגים אותן בחלוניות המידע וברמזי הפרמטרים של פונקציות שהפרמטרים שלהן מוגדרים כ-tuple. איברים אופציונליים ואיברי rest עובדים עם תוויות: [x: number, y?: number], [head: string, ...rest: number[]].

אפשר לעשות push ל-tuple ב-TypeScript?

ב-tuple שניתן לשינוי, כן: push מתקמפל, כי tuples יורשים את המתודות של מערך, למרות שזה שובר את האורך הקבוע. הצהירו על ה-tuple כ-readonly (או צרו אותו עם as const), ו-push, pop והשמה לפי אינדקס יהפכו לשגיאות קומפילציה.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל