I have a method, that should accepts any object, as long as all its fields are strings or numbers
I made this, which works great with duck typing
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("/");
}
and call
interpolateParams("/api/:method/:value", {method: "abc", value: 10});
Now I want to make interpolateParams
to accept any interface for route params
.
interpolateParams<IABCParams>("/api/:method/:value", {method: "abc", value: 10});
Problem is that it still should match constraints for all fields being strings or numbers
Is there a way to specify generic constraint on all fields of given interface to be of certain type?
I tried that
static interpolateParams<T extends {[key: string] : string | number}>(
route: string,
params: T) : string {
and obviously got this
Type 'IABCParams' does not satisfy the constraint '{ [key: string]: string | number; }'.
Index signature is missing in type 'IABCParams'.
Thanks
The where clause in a generic definition specifies constraints on the types that are used as arguments for type parameters in a generic type, method, delegate, or local function. Constraints can specify interfaces, base classes, or require a generic type to be a reference, value, or unmanaged type.
Using type parameters in generic constraints TypeScript allows you to declare a type parameter constrained by another type parameter. The following prop() function accepts an object and a property name. It returns the value of the property.
Interface Type Constraint You can constrain the generic type by interface, thereby allowing only classes that implement that interface or classes that inherit from classes that implement the interface as the type parameter.
When we're using generics, we can use constraints to apply to our generic type parameters. To implement it, all we have to do is use the extends keyword. Before using it, you need to make sure of something — the extends keyword only works with interfaces and classes, otherwise, it will throw an error.
In TypeScript we can apply constraints on Generic type parameters (e.g. T) by using keyword extends (e.g. T extends Serializable). Passing anything other than Shape type to drawShape () function will cause compile time error:
Summary: in this tutorial, you will learn how to develop TypeScript generic interfaces. Like classes, interfaces also can be generic. A generic interface has generic type parameter list in an angle brackets <> following the name of the interface:
A type constraint is a "rule" that narrows down the possibilities of what a generic type could be. For example, in the the send definition above, we declared a type variable T that is not constrained at all. Which is why we were able to call send with values that aren't JSON serializeable. // This compiles ...
A generic interface has generic type parameter list in an angle brackets <> following the name of the interface: interface interfaceName<T> { // ... } Code language: TypeScript (typescript) This make the type parameter T visible to all members of the interface. The type parameter list can have one or multiple types. For example:
T
's constraint can refer to T
(with some restrictions), so you can use a mapped type to generate a constraint that has the same fields as T
:
function interpolateParams<T extends {[P in keyof T] : string | number}>(
route: string,
params: T) : string { /*...*/ }
Beware that trickery with circular type parameter constraints can sometimes cause problems, though this scenario will likely be OK.
Here is final version, thanks Matt for hint
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With