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);
}
