typescript - האם אפשר לבדוק שמפתח של אובייקט הוא מפתח של גנרי?


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

type ResponseGeneric<T> = {
  [K in keyof T]?: {
    otherProp: any
  };
}

function validateGeneric<T>(key: keyof T, value: any): ResponseGeneric<T> {
  return {
    [key]: {
      otherProp: value,
    },
  };
}

אבל עם זה יש לי את השגיאה הבאה ב-validateGeneric:

Type '{ [x: string]: { otherProp: any; }; }' is not assignable to type 'ResponseGeneric<T>'.ts(2322)

אני מודע לכך שאני יכול לעקוף זאת על ידי המרה בעת ההחזרה, אבל תהיתי אם יכול להיות דרך עם גרסת TypeScript העדכנית ביותר (כיום 4.2).

תשובה #1

זה עדיין או באג או מגבלת עיצוב של TypeScript, כפי שמצויין ב-microsoft/TypeScript#13948 microsoft/TypeScript#21030. אם יש לך שם נכס מחושב מסוג גנרי (כמו keyof T), המהדר מרחיב זאת עד string וגורם לאובייקט המתקבל להיות עם סימן אינדקס מסוג string.

לכאורה נעשה עבודה כדי להתמודד עם זה, ב-microsoft/TypeScript#21070, אך זה השפיע יותר ממה שהצוות רצה לקבל, ולכן זה מעולם לא התמזג. ונראה שלא חזרו לזה שוב.

עד שיתבצעו שינויים, תצטרך לעבוד סביב זה. הדרך הקלה ביותר היא, כפי שציינת, להשתמש באסרטציית סוג (זה המונח המועדף; המילה "cast" לעיתים מרמזת על שינויי התנהגות בזמן ריצה, בעוד אסרטציות סוג ב-TypeScript אינן משפיעות כלל בזמן ריצה):

function validateGeneric<K extends PropertyKey>(key: K, value: any) {
  return {
    [key]: {
      otherProp: value,
    },
  } as { [P in K]?: { otherProp: any } };
}

שימו לב שבמקום להשתמש בפרמטר סוג T מסוג אובייקט כלשהו, שיניתי אותו לשימוש רק בסוג מפתח K. ההגדרה שלכם של ResponseGeneric זרקה לחלוטין כל מידע שהיה לה מערכי המאפיינים של T ושמרה רק את המפתחות, אז אפשר באותה מידה לא לנסות להסיק סוג אובייקט כאשר אין כזה.

const result = validateGeneric("a", 123);
/* const result: {
    a?: {
        otherProp: any;
    } | undefined;
} */

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

const kv = <K extends PropertyKey, V>(key: K, val: V) => ({
  [key]: val
}) as K extends any ? { [P in K]: V } : never;

ואז השתמש בפונקציית העזר במקומות שבהם היית כותב בדרך אחרת תכונה מחושבת:

function validateGeneric2<K extends PropertyKey>(key: K, value: any) {
  return kv(key, { otherProp: value })
}

validateGeneric2("a", 123).a.otherProp // okay

קישור למזחלת לקוד

תשובה #2

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

קוד JS:

type ResponseGeneric<T> = {
  [K in keyof T]?: {
    otherProp: any
  };
}

function validateGeneric<T>(key: keyof T, value: any): ResponseGeneric<T> {
  let response: ResponseGeneric<T> = {};

  response[key] = {
    otherProp: value,
  };

  return response;
}

// Example type
type TypeTee = {
  bob: string
}
let teeObject: TypeTee = {
  bob: "Alice"
}

const response: ResponseGeneric<TypeTee> = validateGeneric("bob", "Eve");
console.log("JSON: " + JSON.stringify(response));

פלטי הקוד:

JSON: {"bob":{"otherProp":"Eve"}}

מגרש משחקים ל-JS

קישור למגרש המשחקים