דילוג לתוכן
  • דף הבית
  • קטגוריות
  • פוסטים אחרונים
  • משתמשים
  • חיפוש
  • חוקי הפורום
כיווץ
תחומים

תחומים - פורום חרדי מקצועי

💡 רוצה לזכור קריאת שמע בזמן? לחץ כאן!
  1. דף הבית
  2. תכנות
  3. TS: הגדרת interface לאובייקט המכיל מופעים של קלאס גנרי

TS: הגדרת interface לאובייקט המכיל מופעים של קלאס גנרי

מתוזמן נעוץ נעול הועבר תכנות
12 פוסטים 3 כותבים 318 צפיות
  • מהישן לחדש
  • מהחדש לישן
  • הכי הרבה הצבעות
התחברו כדי לפרסם תגובה
נושא זה נמחק. רק משתמשים עם הרשאות מתאימות יוכלו לצפות בו.
  • מוטי אורןמ מנותק
    מוטי אורןמ מנותק
    מוטי אורן
    השיב לרפאל ב נערך לאחרונה על ידי
    #3

    @רפאל וואו וואו... ממש מצויין בשבילי, אני ינסה ללמוד את החידושים שאני לא מכיר בקוד שלך.

    @רפאל אמר בTS: הגדרת interface לאובייקט המכיל מופעים של קלאס גנרי:

    נאלצתי לנחש חלק מהקוד

    מתנצל... עזרת לי מאוד.

    רפאלר תגובה 1 תגובה אחרונה
    2
    • רפאלר מנותק
      רפאלר מנותק
      רפאל
      השיב למוטי אורן ב נערך לאחרונה על ידי רפאל
      #4

      @מוטי-אורן אתה יכול להשתמש בUtility Type דלהלן כדי להפוך את כל השדות החסרים בBase לOptional:

      /**
       *  Construct a type with the properties of TOptional in which all keys not included in TRequired are marked as optional
       */
      type MarkBaseMissingPropertiesAsOptional<TRequired, TOptional extends TRequired> = Partial<Exclude<TOptional, keyof TRequired>> & Pick<TOptional, keyof TRequired>;
      

      שימוש

      MarkBaseMissingPropertiesAsOptional<UnaryOperands, BinaryOperands>
      

      תוצאה

      {
          dst: string;
          src?: string;
      }
      
      תגובה 1 תגובה אחרונה
      3
      • yossizY מנותק
        yossizY מנותק
        yossiz
        השיב למוטי אורן ב נערך לאחרונה על ידי
        #5

        @מוטי-אורן רציתי להבין את הבעיה והפתרון, אבל נתקעתי כי לא הצלחתי לשחזר את הבעיה

        הנה playground שלא משחזר את הבעיה 😕
        האם תוכל ליצור אחד שכן משחזר?

        📧 יוסי@מייל.קום | 🌎 בלוג | ☕ קפה

        מוטי אורןמ תגובה 1 תגובה אחרונה
        1
        • מוטי אורןמ מנותק
          מוטי אורןמ מנותק
          מוטי אורן
          השיב לyossiz ב נערך לאחרונה על ידי
          #6

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

          להלן הקישור ל-playground המשחזר את הבעיה.

          yossizY רפאלר 2 תגובות תגובה אחרונה
          1
          • yossizY מנותק
            yossizY מנותק
            yossiz
            השיב למוטי אורן ב נערך לאחרונה על ידי
            #7

            @מוטי-אורן נראה לי שכעת זכיתי להבין את הבעיה, עכשיו אני מתקשה בהבנת הפתרון...
            בנוסח שלי, הבעיה היא כזאת:
            הגדרת שתי "חתימות" לפונקציה שלך דרך function overloading. עכשיו הקריאה חייבת להתאים לאחת מהם, והיא לא מתאימה לשום אחד. כי הראשון אומנם מקבל בארגומנט הראשון <Instruction<UnaryOperands (וממילא גם כל דבר שיורש ממנו) אבל בשני הוא מצפה רק ל-dst. והשני מצפה לקבל <Instruction<BinaryOperands ו-Instruction<UnaryOperands | BinaryOperands> לא מתאים לו.

            לא הבנתי איך הפתרון פותר כלום.
            ובנסיונות שלי זה באמת לא פותר את הבעיה.

            מה שכן פותר הוא להוריד את החתימות של ה-overloads ולהשאיר רק את החתימה של ה-implementation, אבל אז ירד לך חלק מה-type safety

            📧 יוסי@מייל.קום | 🌎 בלוג | ☕ קפה

            תגובה 1 תגובה אחרונה
            2
            • רפאלר מנותק
              רפאלר מנותק
              רפאל
              השיב למוטי אורן ב נערך לאחרונה על ידי רפאל
              #8

              להוסיף על דבריו הנכונים של @yossiz:

              נתונים שתי חתימות:

              perform(instruction: Instruction<UnaryOperands>): void;
              perform(instruction: Instruction<BinaryOperands>): void;
              

              החתימה הנכונה תיקבע לפי זהות הפרמטר הניתן לInstruction (הסוג של T), כיוון שהסוג שמועבר בדוגמה לInstruction הוא מסוג Union פתוח של שני הסוגים (UnaryOperands | BinaryOperands) המהדר אינו יכול לקבוע את זהות החתימה הנכונה משום שבזמן הכתיבה לא ניתן לדעת את הנתון הזה (זהות הסוג הספיציפי בתוך הUnion).

              די ברור מדוע הקוד דלהלן אינו תקין:

              interface A { field1: number; }
              interface B { field2: number; }
              
              function perform(param: A): void;
              function perform(param: B): void;
              function perform(param: A | B): void { }
              
              let param: A | B;
              
              perform(param);
              

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

              No overload matches this call.
              
              Overload 1 of 2, '(param: A): void', gave the following error.
                Argument of type 'A | B' is not assignable to parameter of type 'A'.
                  Property 'field1' is missing in type 'B' but required in type 'A'.
              
              Overload 2 of 2, '(param: B): void', gave the following error.
                Argument of type 'A | B' is not assignable to parameter of type 'B'.
                  Property 'field2' is missing in type 'A' but required in type 'B'.ts(2769)
              

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

              yossizY תגובה 1 תגובה אחרונה
              3
              • yossizY מנותק
                yossizY מנותק
                yossiz
                השיב לרפאל ב נערך לאחרונה על ידי yossiz
                #9

                @רפאל אמר בTS: הגדרת interface לאובייקט המכיל מופעים של קלאס גנרי:

                די ברור מדוע הקוד דלהלן אינו תקין:

                interface A { field1: number; }
                interface B { field2: number; }
                
                function perform(param: A): void;
                function perform(param: B): void;
                function perform(param: A | B): void { }
                
                let param: A | B;
                
                perform(param);
                

                כן. פה זה יותר ברור.
                במקרה של @מוטי-אורן לפי איך שהוא הציג את השאלה בהתחלה זה באמת היה אמור לעבוד. כי אצלו ה-interface B יורש מ-interface A ואם כן ה-union שלהם מתאים באמת לחתימה הראשונה כי ה-B הוא גם A. לכן התבלבלתי.

                השאלה שלו הציגה מקרה כזו:

                interface A { field1: number; }
                interface B extends A { field2: number; }
                
                function perform(param: A): void;
                function perform(param: B): void;
                function perform(param: A | B): void { }
                
                let param = { field1: 1 } as A | B;
                
                perform(param);
                

                אגב, מעניין לראות מתוך דוגמאות אלו איזה מהם תקין:

                interface A { field1: number; }
                interface B { field2: number; }
                
                function perform(param: A): void;
                function perform(param: B): void;
                function perform(param: A | B): void { }
                
                function getAOrB() : A | B {
                    return { field1: 1 }
                }
                
                const param1: A | B = { field1: 1 }
                const param2 = { field1: 1 } as A | B
                const param3: A | B = getAOrB()
                const param4: A | B = { field1: 1, field2: 2 }
                
                perform(param1)
                perform(param2)
                perform(param3)
                perform(param4)
                

                הראשון תקין. למה? איפה מתועד התנהגות זו?

                📧 יוסי@מייל.קום | 🌎 בלוג | ☕ קפה

                רפאלר yossizY 3 תגובות תגובה אחרונה
                2
                • רפאלר מנותק
                  רפאלר מנותק
                  רפאל
                  השיב לyossiz ב נערך לאחרונה על ידי
                  #10
                  פוסט זה נמחק!
                  תגובה 1 תגובה אחרונה
                  0
                  • רפאלר מנותק
                    רפאלר מנותק
                    רפאל
                    השיב לyossiz ב נערך לאחרונה על ידי
                    #11

                    @yossiz אמר בTS: הגדרת interface לאובייקט המכיל מופעים של קלאס גנרי:

                    לכן התבלבלתי.

                    אתה ממש לא. התעלמתי לגמרי מהירושה. אתה פשוט צודק.

                    תגובה 1 תגובה אחרונה
                    2
                    • yossizY מנותק
                      yossizY מנותק
                      yossiz
                      השיב לyossiz ב נערך לאחרונה על ידי yossiz
                      #12

                      @yossiz אמר בTS: הגדרת interface לאובייקט המכיל מופעים של קלאס גנרי:

                      הראשון תקין. למה? איפה מתועד התנהגות זו?

                      נראה לי שהבנתי, יש שתי רמות של טייפים ב-TS, יש את ה-declared type וה-observed type. ה-declared type משפיע על ה-assignability וה-observed type משפיע על השימוש בפועל בערך.
                      השמה של ערך לתוך משתנה יכול להשפיע על ה-observed type על ידי מנגנון שנקרא type narrowing.
                      דוגמה:

                      // declared type
                      let x: string | number
                      // observed type becomes number (type narrowing by assignment)
                      x = 1.0
                      // legal to use as number
                      x.toFixed()
                      // still fine to reassign to string
                      x = "Hi"
                      // observed type is now string (again type narrowing by assignment)
                      x.charAt(1);
                      // coerced to string | number
                      x = x as string | number
                      // illegal!
                      // Property 'charAt' does not exist on type 'string | number'.
                      //   Property 'charAt' does not exist on type 'number'.(2339)
                      x.charAt(1)
                      

                      מקור:
                      https://www.typescriptlang.org/docs/handbook/2/narrowing.html#assignments

                      לכן בשורה זו:

                      const param1: A | B = { field1: 1 }
                      

                      למרות שה-declared type הוא A | B אבל יש מיד type narrowing על ידי ההשמה.

                      📧 יוסי@מייל.קום | 🌎 בלוג | ☕ קפה

                      תגובה 1 תגובה אחרונה
                      4

                      בא תתחבר לדף היומי!
                      • התחברות

                      • אין לך חשבון עדיין? הרשמה

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