How to constrain or control a generic type parameter using another generic type parameter in TypeScript?

January 17, 2022 - 4 min read

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 😃!

Feel free to share if you found this useful 😃.