TypeScript - איך להגדיר סוג בתוך ממשק גנרי עבור תכונה בתוך סוג גנרי זה


דוגמה :

export interface Column<T> {
    field: string;
    columnFormatter?: (props: {
        value: any/** field type inside T**/; data: T; node: any
    }) => void;
}

field הוא שם התכונה בתוך סוג T, איך אני יכול לומר שהערך הוא מהסוג הזה?

export interface IPurchase {
    id: string;
    name: string;
    purchaseDate: Date;
}

let doSomethingWithMyDate: (myDate: Date) => (true);


const columns: Array<Column<IPurchase>> = [
    {
        field: "purchaseDate", /* "purchaseDate" must be inside IPurchase */
        columnFormatter: ({ value /* must identify that this is a Date */ }) => 
            doSomethingWithMyDate(value)

    }];

תשובה #1

כדי לייצג את הקורלציה בין מאפיין field וסוג מאפיין value של הפרמטר props ל-columnFormatter, אתה צריך ש-Column<T> יהיה סוג איחוד עם חבר אחד עבור כל מפתח של T. לדוגמה, בהתבסס על הדוגמה שלך של IPurchase, אתה צריך ש-Column<IPurchase> יהיה

type ColumnIPurchase = {
    field: "id";
    columnFormatter?: ((props: {
        value: string;
        data: IPurchase;
        node: any;
    }) => void);
} | {
    field: "name";
    columnFormatter?: ((props: {
        value: string;
        data: IPurchase;
        node: any;
    }) => void) 
} | {
    field: "purchaseDate";
    columnFormatter?: ((props: {
        value: Date;
        data: IPurchase;
        node: any;
    }) => void);
}

זה יתנהג כפי שרצוי:

const columns: Array<Column<IPurchase>> = [
    {
        field: "purchaseDate",
        columnFormatter: ({ value }) => doSomethingWithMyDate(value)
    },
    {
        field: "name",
        columnFormatter: ({ value }) => doSomethingWithMyDate(value) // error!
        //  string isn't a Date ----------------------------> ~~~~~ 
    }
];

אז זה מה שאנחנו רוצים... איך אנחנו יכולים לכתוב Column<T> כדי לעשות את זה?


הנה דרך אחת:

type Column<T> = { [K in keyof T]-?: {
    field: K;
    columnFormatter?: (props: { value: T[K]; data: T; node: any }) => void;
} }[keyof T]

הצורה הכללית של סוג זה, {[K in keyof T]-?: F<K>}[keyof T], ידועה כ-סוג אובייקט מפוצל, כמונח שהוטבע ב-microsoft/TypeScript#47109; אנו יוצרים סוג ממופה על המפתחות ב-keyof T ואז מיד מאינדקסים לתוכו עם keyof T כדי לקבל איחוד של F<K> עבור כל מפתח K ב-keyof T.

במיוחד כאן אנו מחשבים { field: K; columnFormatter?: (props: { value: T[K]; data: T; node: any }) => void; } כאשר K הוא סוג המפתח, ו-T[K] הוא סוג ערך המאפיין התואם לאותו מפתח.

וכלומר אתה יכול לוודא ש‑Column<IPurchase> מעריכה בדיוק לסוג הרצוי.

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