One key aspect of Generics is "Passing Type Arguments," which empowers us to create highly adaptable functions, classes, and interfaces that cater to a wide range of data types. In this piece of article, we will embark on a journey to understand the ins and outs of Passing Type Arguments, exploring its various applications and how it enhances code maintainability.

Add Type Parameters to a Function:

TypeScript allows us to add type parameters to functions using angle brackets '<>' and a single uppercase letter to represent the type parameter. For example:

function echo<T>(arg: T): T {
  return arg;
}

In this example, T is the type parameter, and it denotes the generic type that the function can work with.

Defaults in Type Parameters:

We can assign default types to type parameters to provide more flexibility and handle scenarios where a specific type is not explicitly provided. This can be achieved by specifying the default type after the type parameter:

function getValueOrDefault<T = string>(value: T, defaultValue: T): T {
  return value || defaultValue;
}

Here, if the type T is not explicitly provided during the function call, it defaults to string.

Infer Types from Type Arguments:

TypeScript has a remarkable ability to infer types from the arguments passed to generic functions. This allows us to write cleaner code without explicitly specifying type parameters.

function concatenate<T>(arr: T[], separator: string): string {
  let result = arr.join(separator);
  console.log(result)
  return result
}

const fruits = ["apple", "banana", "orange", "kivi"];
const concatenatedFruits = concatenate(fruits, ", "); // Output: "apple, banana, orange, kivi"

In this example, TypeScript infers that the generic type T is string, as the array fruits contains string elements.

Avoid "any" Types with Generics:

Generics provide a powerful alternative to using the any type, as they offer the flexibility of handling different data types while retaining type safety.

const fetchData = async <T>(url: string) => {
  const data: T = await fetch(url).then((response) => response.json());
  return data;
};

Here, the function fetchData maintains type safety without resorting to using any.

Passing Type Arguments

Imagine we are building an e-commerce platform with a shopping cart that handles various product types. Using Generics, we can write functions that are adaptable to different product data structures.