Beginner's Guide to TypeScript Generics: Writing Reusable Functions and Classes

Learn how to use TypeScript generics to create reusable and type-safe functions and classes. This beginner-friendly guide includes practical examples.

TypeScript generics allow you to write flexible, reusable code components that work with multiple types instead of a single one. This helps improve code maintainability and safety by preserving type information across functions and classes.

In this guide, we'll explore the basics of TypeScript generics by building reusable functions and classes. Let's start with a simple generic function.

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

// Usage:
let output1 = identity<string>("hello");
let output2 = identity<number>(42);

console.log(output1); // Output: hello
console.log(output2); // Output: 42

Here, `identity` is a generic function with type parameter `T`. Whatever type you provide, the function receives and returns a value of that type. This means you get type safety while keeping the function reusable.

You can also let TypeScript infer the type argument based on the passed value, so it's not always necessary to specify the type explicitly.

typescript
let output3 = identity("inferred"); // TypeScript infers that T is string
console.log(output3); // Output: inferred

Next, let's create a generic class to understand how generics apply there. Imagine a simple container that can hold a value of any type.

typescript
class Container<T> {
  private value: T;

  constructor(value: T) {
    this.value = value;
  }

  getValue(): T {
    return this.value;
  }

  setValue(newValue: T): void {
    this.value = newValue;
  }
}

// Usage:
const stringContainer = new Container<string>("my string");
console.log(stringContainer.getValue()); // Output: my string

const numberContainer = new Container<number>(100);
console.log(numberContainer.getValue()); // Output: 100

The `Container` class uses a generic type parameter `T` to store a value of any type. This way, you can create multiple instances of this container for different types, all while preserving type safety.

Generics can also work with multiple type parameters. For example, a generic pair class can hold two different types.

typescript
class Pair<T, U> {
  constructor(public first: T, public second: U) {}

  getPair(): [T, U] {
    return [this.first, this.second];
  }
}

const pair = new Pair<string, number>("age", 30);
console.log(pair.getPair()); // Output: [ 'age', 30 ]

Using generics helps avoid the need to write multiple versions of functions or classes for different types, making your code more concise and easier to maintain.

In summary, generics in TypeScript enable you to create reusable, type-safe functions and classes by parameterizing types. Start using generics in your projects to make your code more robust and flexible.