Skip to content

Generics Deep Dive

Generics are a powerful feature in TypeScript. They let you create reusable functions, components, and classes that can work with many different types, while still keeping type safety. In this section, we will learn how to use generics the right way, including basic generic functions, multiple type parameters, generic constraints, and generic classes. By the end of this section, you will understand how to use generics to write flexible and easy-to-maintain TypeScript code.

Generics let you write one function or class that can work with many different types, instead of writing the same code again and again for each type. It still keeps type safety, which means TypeScript still checks everything properly.

function identity<T>(value: T): T {
return value;
}

Meaning:

  • the input type is T
  • the return type is also T

So whatever type goes in, the exact same type comes out. Nothing changes.

function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}

Use this when the two values you are working with can be different types from each other.

function logLength<T extends { length: number }>(value: T): void {
console.log(value.length);
}

Here, extends is used to set a rule. Now T is not just any type, it must have a length property, otherwise TypeScript will not allow it.

class Box<T> {
value: T;
constructor(value: T) {
this.value = value;
}
}
const numberBox = new Box<number>(123);
const stringBox = new Box<string>("hello");

This is the same idea as before, but now used with a class instead of a function. The Box class can hold any type you give it.

type ApiSuccess<T> = { status: "success"; data: T };
type ApiError = { status: "error"; message: string };
type ApiResponse<T> = ApiSuccess<T> | ApiError;
async function fetchData<T>(url: string): Promise<ApiResponse<T>> {
const response = await fetch(url);
if (!response.ok) {
return { status: "error", message: "Request failed" };
}
const data = (await response.json()) as T;
return { status: "success", data };
}

This is a common real-world example. The fetchData function can be used to fetch any kind of data from an API, and you simply tell it what type to expect using T.

graph LR A[Generic Function] --> B[Input Type T] B --> C[Reusable Logic] C --> D[Return with Same T]