In order to control or constrain a generic type parameter using another generic type parameter, we can use the extends
keyword after the name of the generic type parameter to be controlled or constrained, followed by the name of the other generic type parameter type and the constraint to be applied.
TL;DR
// a generic function to search for a value within an array
// also constraining the Type2 generic type paramter with whatever values type
// the Type1 generic type paramter will hold
function searchArr<Type1, Type2 extends Type1>(
arr: Type1[],
valueToSearch: Type2
) {
return arr.indexOf(valueToSearch); // Valid ✅.
}
/* call the function with different array values type */
// ✅ Valid function calls
searchArr(["John", "Lily", "Roy"], "Lily");
searchArr([1, 2, 3, 4], 5);
// ❌ Error since the value to search is of another
// type and not of type of the values in the array
searchArr(["John", "Lily", "Roy"], 1);
For example, let's say we have a function that takes 2
arguments where the first argument is an array and the second argument is a value to search within the array. The function returns the index of the item in the array.
The logic of the function may look like this, wihtout any types,
// function to search for a value within an array
function searchArr(arr, valueToSearch) {
return arr.indexOf(valueToSearch);
}
Now let's make 2 generic type parameters called Type1
and Type2
within the angled brackets syntax (<>
) and then assign those generic type parameters respectively as the types for the arr
parameter and the valueToSearch
parameter.
The arr
parameter needs to have a type of Type1[]
since this parameter is an array.
This is done to make our searchArr
function into a reusable generic function.
it can be done like this,
// a generic function to search for a value within an array
function searchArr<Type1, Type2>(arr: Type1[], valueToSearch: Type2) {
// Error ❌. Argument of type 'Type2' is not assignable to parameter of type 'Type1'.
return arr.indexOf(valueToSearch);
}
The above code looks fine but the TypeScript compiler will start showing an error saying that the Argument of type 'Type2' is not assignable to parameter of type 'Type1'.
.
This is because since we are using different generic type parameters
for both arr
and the valueToSearch
parameters (namely Type1
and Type2
), the TypeScript compiler doesn't know beforehand which type to use and the types for these 2 parameters can be different whenever this function is called or invoked.
This is where the concept of constraining one of the generic type parameters to the other comes into play.
In our case since the first parameter arr
can accept an array of any type then we should limit the valueToSearch
parameter type to be of the same type as the values in the array.
To achive this functionality let's use the extends
keyowrd after the Type2
generic type parameter adn then associate the Type1
generic type parameter like this,
// a generic function to search for a value within an array
// also constraining the Type2 generic type paramter with whatever values type
// the Type1 generic type paramter will hold
function searchArr<Type1, Type2 extends Type1>(
arr: Type1[],
valueToSearch: Type2
) {
return arr.indexOf(valueToSearch); // Valid ✅.
}
Now, as soon as we constraint the Type2
with Type1
generic parameter type the error is gone and the TypeScript compiler is happy with it.
Now let's call the searchArr
function with differnet kind of array values type liek this,
// a generic function to search for a value within an array
// also constraining the Type2 generic type paramter with whatever values type
// the Type1 generic type paramter will hold
function searchArr<Type1, Type2 extends Type1>(
arr: Type1[],
valueToSearch: Type2
) {
return arr.indexOf(valueToSearch); // Valid ✅.
}
/* call the function with different array values type */
// ✅ Valid function calls
searchArr(["John", "Lily", "Roy"], "Lily");
searchArr([1, 2, 3, 4], 5);
// ❌ Error since the value to search is of another
// type and not of type of the values in the array
searchArr(["John", "Lily", "Roy"], 1);
As you can see from the above code the first 2 function calls are valid as it satisfies generic type parameter constraints and the last call is an error.
We have successfully constrained the generic type parameter with another generic type parameter. Yay 🥳!
See the above code live in codesandbox.
That's all 😃!