מילון - Typescript Map> "No overload matches this call", אבל אני לא מבין למה?


אני יוצר משחק לוח ב-TypeScript. בו אני מצהיר על הדברים הבאים:

export enum PieceType {
    PAWN,
    KNIGHT2,
    KNIGHT4,
    WIZARD,
    KING
}

export class Board {
    // ...
    private takeRules: Map<PieceType, Set<PieceType>> = new Map([
        [PieceType.PAWN, new Set([PieceType.PAWN, PieceType.KNIGHT2, PieceType.KNIGHT4, PieceType.WIZARD])],
        [PieceType.KNIGHT2, new Set([PieceType.PAWN, PieceType.KNIGHT2, PieceType.KNIGHT4, PieceType.WIZARD, PieceType.KING])],
        [PieceType.KNIGHT4, new Set([PieceType.PAWN, PieceType.KNIGHT2, PieceType.KNIGHT4, PieceType.WIZARD, PieceType.KING])],
        [PieceType.KING, new Set([PieceType.PAWN, PieceType.KNIGHT2, PieceType.KNIGHT4])]
    ]);
    private enchantRules: Map<PieceType, Set<PieceType>> = new Map([
        [PieceType.WIZARD, new Set([PieceType.PAWN, PieceType.KNIGHT2, PieceType.KNIGHT4])],
        [PieceType.KING, new Set([PieceType.WIZARD])]
    ]);
    // ...
}

שגיאה זו מתקבלת באופן הבא:

[tsl] ERROR in ./src/board.ts(28,60)
      TS2769: No overload matches this call.
  Overload 1 of 3, '(iterable: Iterable<readonly [PieceType.WIZARD | PieceType.KING, Set<PieceType.PAWN | PieceType.KNIGHT2 | PieceType.KNIGHT4>]>): Map<PieceType.WIZARD | PieceType.KING, Set<...>>', gave the following error.
    Argument of type '([PieceType.WIZARD, Set<PieceType.PAWN | PieceType.KNIGHT2 | PieceType.KNIGHT4>] | [PieceType.KING, Set<PieceType.WIZARD>])[]' is not assignable to parameter of type 'Iterable<readonly [PieceType.WIZARD | PieceType.KING, Set<PieceType.PAWN | PieceType.KNIGHT2 | PieceType.KNIGHT4>]>'.
      The types returned by '[Symbol.iterator]().next(...)' are incompatible between these types.
        Type 'IteratorResult<[PieceType.WIZARD, Set<PieceType.PAWN | PieceType.KNIGHT2 | PieceType.KNIGHT4>] | [PieceType.KING, Set<PieceType.WIZARD>], any>' is not assignable to type 'IteratorResult<readonly [PieceType.WIZARD | PieceType.KING, Set<PieceType.PAWN | PieceType.KNIGHT2 | PieceType.KNIGHT4>], any>'.
          Type 'IteratorYieldResult<[PieceType.WIZARD, Set<PieceType.PAWN | PieceType.KNIGHT2 | PieceType.KNIGHT4>] | [PieceType.KING, Set<PieceType.WIZARD>]>' is not assignable to type 'IteratorResult<readonly [PieceType.WIZARD | PieceType.KING, Set<PieceType.PAWN | PieceType.KNIGHT2 | PieceType.KNIGHT4>], any>'.
            Type 'IteratorYieldResult<[PieceType.WIZARD, Set<PieceType.PAWN | PieceType.KNIGHT2 | PieceType.KNIGHT4>] | [PieceType.KING, Set<PieceType.WIZARD>]>' is not assignable to type 'IteratorYieldResult<readonly [PieceType.WIZARD | PieceType.KING, Set<PieceType.PAWN | PieceType.KNIGHT2 | PieceType.KNIGHT4>]>'.
              Type '[PieceType.WIZARD, Set<PieceType.PAWN | PieceType.KNIGHT2 | PieceType.KNIGHT4>] | [PieceType.KING, Set<PieceType.WIZARD>]' is not assignable to type 'readonly [PieceType.WIZARD | PieceType.KING, Set<PieceType.PAWN | PieceType.KNIGHT2 | PieceType.KNIGHT4>]'.
                Type '[PieceType.KING, Set<PieceType.WIZARD>]' is not assignable to type 'readonly [PieceType.WIZARD | PieceType.KING, Set<PieceType.PAWN | PieceType.KNIGHT2 | PieceType.KNIGHT4>]'.
                  Types of property '1' are incompatible.
                    Type 'Set<PieceType.WIZARD>' is not assignable to type 'Set<PieceType.PAWN | PieceType.KNIGHT2 | PieceType.KNIGHT4>'.
                      Type 'PieceType.WIZARD' is not assignable to type 'PieceType.PAWN | PieceType.KNIGHT2 | PieceType.KNIGHT4'.
  Overload 2 of 3, '(entries?: readonly (readonly [PieceType.WIZARD | PieceType.KING, Set<PieceType.PAWN | PieceType.KNIGHT2 | PieceType.KNIGHT4>])[]): Map<PieceType.WIZARD | PieceType.KING, Set<...>>', gave the following error.
    Type 'Set<PieceType.WIZARD>' is not assignable to type 'Set<PieceType.PAWN | PieceType.KNIGHT2 | PieceType.KNIGHT4>'.

אם אני שם את המפה השנייה בהערות: אין בעיה. אם אני משמיט את enum הקוסם בשורה האחרונה של המפה השנייה: אין בעיה. אני אפילו יכול לשים את הערך שהושמט בשימוש בקונסטרקטור בלי בעיה. לדוגמה.

    private takeRules: Map<PieceType, Set<PieceType>> = new Map([
        [PieceType.PAWN, new Set([PieceType.PAWN, PieceType.KNIGHT2, PieceType.KNIGHT4, PieceType.WIZARD])],
        [PieceType.KNIGHT2, new Set([PieceType.PAWN, PieceType.KNIGHT2, PieceType.KNIGHT4, PieceType.WIZARD, PieceType.KING])],
        [PieceType.KNIGHT4, new Set([PieceType.PAWN, PieceType.KNIGHT2, PieceType.KNIGHT4, PieceType.WIZARD, PieceType.KING])],
        [PieceType.KING, new Set([PieceType.PAWN, PieceType.KNIGHT2, PieceType.KNIGHT4])]
    ]);
    private enchantRules: Map<PieceType, Set<PieceType>> = new Map([
        [PieceType.WIZARD, new Set([PieceType.PAWN, PieceType.KNIGHT2, PieceType.KNIGHT4])],
        [PieceType.KING, new Set([])]
    ]);

   // ...

    constructor() {
        // ...
        this.enchantRules.get(PieceType.KING).add(PieceType.WIZARD)
    }

אני באמת לא מבין איך זה אפשרי?

תשובה #1

שאלה מאוד מעניינת. הנה הפתרון:

export enum PieceType {
    PAWN,
    KNIGHT2,
    KNIGHT4,
    WIZARD,
    KING
}

export class Board {
    // ...
    private takeRules = new Map<PieceType, Set<PieceType>>([
        [PieceType.PAWN, new Set([PieceType.PAWN, PieceType.KNIGHT2, PieceType.KNIGHT4, PieceType.WIZARD])],
        [PieceType.KNIGHT2, new Set([PieceType.PAWN, PieceType.KNIGHT2, PieceType.KNIGHT4, PieceType.WIZARD, PieceType.KING])],
        [PieceType.KNIGHT4, new Set([PieceType.PAWN, PieceType.KNIGHT2, PieceType.KNIGHT4, PieceType.WIZARD, PieceType.KING])],
        [PieceType.KING, new Set([PieceType.PAWN, PieceType.KNIGHT2, PieceType.KNIGHT4])]
    ]);
    private enchantRules = new Map<PieceType, Set<PieceType>>([
        [PieceType.WIZARD, new Set([PieceType.PAWN, PieceType.KNIGHT2, PieceType.KNIGHT4])],
        [PieceType.KING, new Set([PieceType.WIZARD])]
    ])
}

רק הגדרתי גנריים עבור Map במקום להגדיר את כל הסוג: new Map<PieceType, Set<PieceType>>.

אבל השאלה העיקרית, מדוע התקלה התרחשה?

בואו נעשה קצת ניתוח של:

   private enchantRules: Map<PieceType, Set<PieceType>> = new Map([
        [PieceType.WIZARD, new Set([PieceType.PAWN, PieceType.KNIGHT2, PieceType.KNIGHT4])],
        [PieceType.KING, new Set([PieceType.WIZARD])]
    ]);

אם תשים הגדרת טיפוס Map<PieceType, Set<PieceType>> לפני סימן ה-equal, TypeScript ינסה להסיק את הטיפוס הזה. לכן, אם תשים ערך מקטגוריית PieceType שלא קיים בישות הקודמת, זה יגרום לשגיאה.

תסתכל על דוגמה פשוטה יותר:

type Fst = PieceType.PAWN | PieceType.KNIGHT2 | PieceType.KNIGHT4
type Scd = PieceType.WIZARD

let fst: Fst = PieceType.PAWN;
let scd: Scd = PieceType.WIZARD;

scd = fst; // error

לא ניתן להקצות את fst ל-scd, אבל אם תוסיף את סוג המניין PAWN לסוג Scd, זה יעבוד:

type Fst = PieceType.PAWN | PieceType.KNIGHT2 | PieceType.KNIGHT4
type Scd = PieceType.WIZARD | PieceType.PAWN

let fst: Fst = PieceType.PAWN;
let scd: Scd = PieceType.WIZARD;

scd = fst; // ok

בואו נחזור לבעיה שלנו. מכיוון ש-WIZARD לא הוגדר בערך הראשון במפה V, אינכם יכולים להשתמש ב-WIZARD כלל.

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

נ.ב. ייתכן שתמצא גם את התשובה הזו מעניינת

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