Typescript keyof generic ȘI extinde un tip


Având următoarea interfață:

interface Payload {
  value1: number;
  value2: string;
}

Vreau să creez o funcție care acceptă Payload[] și doar cheile din Payload care extind number. Ceva de genul acesta:

function F<T, K extends keyof T, T[K] extends number>(arr: T[], property: K) {
  //
}

function foo(payloads: Payload[]) {
  F(payloads, "value1"); // should work
  F(payloads, "value2"); // should not work because value2 is a string
  F(payloads, "value3"); // should not work because value3 not in Payload[]
}

Vezi locul de joacă

Răspuns #1

Am reușit să fac asta să funcționeze folosind un tip mapat care păstrează doar proprietățile number și number | undefined:

interface Payload {
    value1: number;
    value2: string;
    value3?: number;
    value4?: string;
}

type NumberOnly<T> = {
    // Doesn't allow `number | undefined` if `strictNullChecks` is enabled
    //[key in keyof T as T[key] extends number ? key : never]: T[key];
    // This will always allow `number | undefined` on top of just `number`
    [key in keyof T as T[key] extends number | undefined ? key : never]: T[key];
};

function F<T extends NumberOnly<any>, K extends keyof NumberOnly<T>>(arr: T[], property: K) {
    const value = arr[0][property];
    return value;
}

function foo(payloads: Payload[]) {
    F(payloads, "value1"); // should work
    F(payloads, "value2"); // should not work because value2 is a string
    F(payloads, "value3"); // should work (you can filter undefined if you want)
    F(payloads, "value4"); // should not work because value4 is a string
    F(payloads, "value5"); // should not work because value5 not in Payload[]
}

din punct de vedere tehnic ar accepta de asemenea value5: undefined, dar nu de exemplu value5: string | undefined, un caz limită care nu ar trebui să conteze cu adevărat.

Acceptă doar value1 și value3:

bazat pe acest răspuns folosind o caracteristică TypeScript 4.1