אופרטור הפצה שמסיר הגדרת טיפוס ב-Typescript
נתקלתי בתופעה מוזרה ב-TypeScript. כאשר משתמשים במפעיל הפיזור (spread operator), נראה שהבדיקה של סוגי הנתונים אובדת. הייתי מצפה שזה יגרום לשגיאה:
interface Input {
thingOne: string;
thingTwo: number;
thingThree: string;
thingFour: boolean;
thingFive: Array<number>
}
interface Output {
thingThree: string;
thingFour: boolean;
thingFive: Array<number>
}
const transformFunction = (input: Input): Output => {
const result: Output = {
...input,
thingFour: !input.thingFour
}
return result
}
דוגמה למזח משחק של TypeScript
אני מקליד result כ־Output, אבל פיזור אובייקט מסוג Input לתוכו ברור שייצור תוצאה שאינה מהסוג Output. למה TypeScript לא תופס את זה?
תשובה #1
מה שאנחנו מצפים כאן הוא להפעיל את תכונת בדיקת תכונות מיותרות של TypeScript. תכונה זו אומרת לנו ש-TypeScript יזהיר אותנו אם יש תכונות נוספות באובייקט אם אנו מציינים במפורש את סוג האובייקט. למרבה הצער, זה עובד רק עבור ספריות אובייקט.
לדוגמה זה:
const transformFunction = (input: Input): Output => {
return {
thingOne: "string;",
thingTwo: 123,
thingThree: "string;",
thingFour: !input.thingFour,
thingFive: [1, 23],
};
};
מזהיר :
Type '{ thingOne: string; thingTwo: number; thingThree: string; thingFour: boolean; thingFive: number[]; }' is not assignable to type 'Output'.
Object literal may only specify known properties, and 'thingOne' does not exist in type 'Output'.
נראה שמקרה המפעיל spread אינו מכוסה תחת בדיקת תכונה נוספת. ראה כאן: https://github.com/microsoft/TypeScript/issues/41237
זו הבדיקה לנכסים עודפים. לפי @RyanCavanaugh זה יותר כמו לינטר, לא ממש חלק ממערכת הסוגים. אבל לא כל מקרה מכוסה על ידי בדיקה זו, כפי ששמת לב.
הם גם הזכירו לבדוק המרה מתמשכת על סוגים מדויקים כאן: https://github.com/microsoft/TypeScript/issues/12936
תשובה #2
הפונקציה נכונה מבחינת סוגים מכיוון שניתן להקצות את סוג Input לסוג Output ללא בעיות, אפילו שיש לו תכונות נוספות. עבור אובייקטים מילוליים בלבד, יש בדיקת תכונות עודפות, מכיוון שתכונות עודפות באובייקט מילולי סביר מאוד שיהיו טעויות. שקול דוגמה זו של קבוע.
const o: {a: number} = {a: 1, b: 2}
כיוון של־o עכשיו יש טיפוס {a: number}, אין דרך ממוטבת לגשת לנכס b, ולכן TypeScript מתייחס לזה כטעות.
