DEV Community

Cover image for Mastering TypeScript Generics for Reusable Logic
Abhay Kumar
Abhay Kumar

Posted on • Updated on

Mastering TypeScript Generics for Reusable Logic

When developing with TypeScript, one of the most powerful tools at your disposal is the ability to write reusable and flexible code. Generics play a crucial role in achieving this by allowing you to create components, functions, and classes that can work with a wide variety of types while still maintaining strict type safety. In this blog post, we'll dive deep into how you can leverage generics to create reusable logic in TypeScript.

What Are Generics?

Generics are a way to create components, functions, or classes that work with different data types without sacrificing type safety. Instead of specifying a single type, generics allow you to define a placeholder type that can be replaced with a specific type when the function or class is used.

Why Use Generics?

  • Flexibility: Write code that can handle any type.
  • Type Safety: Ensure that your code works with specific types while avoiding the pitfalls of any.
  • Reusability: Reduce code duplication by creating components that can work with multiple types.

Understanding Generics with Functions

One of the most common places where you'll encounter generics is in functions. Let's start with a simple example.

Basic Generic Function:

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

const numberValue = identity(42); // T is inferred as number
const stringValue = identity("Hello"); // T is inferred as string

Enter fullscreen mode Exit fullscreen mode

In this example, the identity function is defined with a generic type parameter T. This means that T can be any type, and the function will return a value of the same type that was passed in.

Type Inference:

TypeScript is smart enough to infer the type of T based on the argument you pass to the function. When you pass 42, TypeScript infers that T is number, and when you pass "Hello", T is inferred as string.

Constraining Generics

While the flexibility of generics is powerful, sometimes you want to constrain the types that can be passed to a generic function. This is where type constraints come into play.

Using Constraints with Generics:

function getLength<T extends { length: number }>(value: T): number {
    return value.length;
}

const stringLength = getLength("TypeScript"); // Works, returns 10
const arrayLength = getLength([1, 2, 3]);     // Works, returns 3
// const numberLength = getLength(42); // Error: Argument of type '42' is not assignable to parameter of type '{ length: number; }'.

Enter fullscreen mode Exit fullscreen mode

In this example, we’ve constrained the generic type T to types that have a length property (such as string or array). This way, you can ensure that only types with a length property are passed to the getLength function, preventing potential runtime errors.

Generics in Interfaces

You can also use generics in interfaces, allowing you to define contracts that can be applied to multiple types.

Generic Interface Example:

interface Pair<K, V> {
    key: K;
    value: V;
}

const numberStringPair: Pair<number, string> = {
    key: 1,
    value: "TypeScript",
};

const stringBooleanPair: Pair<string, boolean> = {
    key: "isActive",
    value: true,
};

Enter fullscreen mode Exit fullscreen mode

Here, the Pair interface takes two generic types K and V. This allows you to create pairs of different types, such as a number key with a string value or a string key with a boolean value.

Generics are a fundamental feature of TypeScript that allow you to create flexible, reusable, and type-safe code. By understanding how to use generics in functions, classes, and interfaces, you can write more versatile code that adapts to different types without compromising type safety.

🔗 Connect with me on LinkedIn:
Let's connect and discuss more about Typescript, web development, and performance enhancement!

LinkedIn Profile:Abhay Kumar

React #WebDevelopment #Generics #TypeScript #Frontend

Top comments (0)