Begränsningar för gränssnittsmedlemmar i TypeScript-generiska typer


Jag har en metod som bör acceptera vilket objekt som helst, så länge alla dess fält är strängar eller siffror

Jag gjorde detta, vilket fungerar utmärkt med and-typning

static interpolateParams(
    route: string, 
    params: {[key: string] : string | number}) : string {

    const parts = route
        .split("/")
        .map(part => {
            const match = part.match(/:([a-zA-Z09]*)\??/);
            if (match) {
                if (!params[match[1]]) {
                    console.error("route argument was not provided", route, match[1]);
                    return part;
                }

                return params[match[1]];
            }
            else {
                return part;
            }
        })

    return "/" + parts.slice(1).join("/");
}

och ring

interpolateParams("/api/:method/:value", {method: "abc", value: 10});

Nu vill jag göra så att interpolateParams kan acceptera vilket gränssnitt som helst för ruttens params.

interpolateParams<IABCParams>("/api/:method/:value", {method: "abc", value: 10});

Problemet är att det fortfarande bör uppfylla begränsningarna för att alla fält ska vara strängar eller siffror

Finns det ett sätt att specificera en generisk begränsning för alla fält i ett givet gränssnitt så att de är av en viss typ?

Jag försökte det

static interpolateParams<T extends {[key: string] : string | number}>(
    route: string, 
    params: T) : string {

och tydligen fick detta

Typen 'IABCParams' uppfyller inte begränsningen '{ [key: string]: string | number; }'.

Indexsignatur saknas i typen 'IABCParams'.

Tack

Svar #1

T:s begränsning kan hänvisa till T (med vissa begränsningar), så du kan använda en mappad typ för att generera en begränsning som har samma fält som T:

function interpolateParams<T extends {[P in keyof T] : string | number}>(
    route: string, 
    params: T) : string { /*...*/ }

Var medveten om att trick med cirkulära typparametervillkor ibland kan orsaka problem, även om detta scenario sannolikt kommer att vara okej.

Svar #2

Här är den slutliga versionen, tack Matt för tipset

static interpolateParams(
    route: string, 
    params: {[key: string] : string | number}) : string {

    const parts = route
        .split("/")
        .map(part => {
            const match = part.match(/:([a-zA-Z09]*)\??/);
            if (match) {
                if (!params[match[1]]) {
                    if (part.endsWith("?")) {
                        return null;
                    }

                    console.error("route argument was not provided", route, match[1]);
                    return part;
                }

                return params[match[1]];
            }
            else {
                return part;
            }
        }).filter(p => p && p != "");

    return "/" + parts.join("/");
}

static formatRoute<T extends {[P in keyof T] : string | number}>(
    route: string,
    params: T
) : string {
    return interpolateParams(route, params);
}

Relaterade artiklar