Mahamudul Hasan Rubel
HomeAboutProjectsSkillsExperienceBlogPhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • About
  • Projects
  • Skills
  • Experience
  • Blog
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
TypeScriptJavaScriptJune 20, 20264 min read

Generics in TypeScript that actually pay off for your codebase

Generics in TypeScript can feel like an academic hurdle, but they pay off when you use them to enforce type safety in API calls and reusable components.

TypeScriptJavaScriptProgrammingSoftware EngineeringWeb DevelopmentFrontend
A vibrant workspace showing computer monitors with code, keyboard, and tech accessories.

I spent the first six months of my TypeScript journey treating <T> like a magical incantation I should avoid at all costs. Every time I saw a complex generic signature, I assumed the developer was just showing off. It wasn't until I had to refactor a massive API client for a dashboard—where we were manually casting any everywhere—that I realized generics weren't just for library authors. They’re for anyone who wants to stop fighting the compiler and start letting it do the heavy lifting.

Why Generics in TypeScript Matter for Real Projects

The core problem with standard TypeScript interfaces is that they’re rigid. If you write a function to fetch data from an endpoint, you either write one for every single resource, or you return any. Neither is sustainable. When I was working on React Performance Patterns You Actually Need in 2025, I noticed that our data fetching layer was the biggest source of "type-blindness." We were losing all our type safety the moment the JSON hit our state management.

Generics allow you to write a function that "remembers" what it was given. Instead of returning a generic Object, you tell the function, "Whatever type you get, return that back to me."

The "Before" vs. The "After"

We started with a standard fetchData utility that looked like this:

TYPESCRIPT
async function fetchData(url: string) {
  const response = await fetch(url);
  return response.json(); // Returns any
}

const user = await fetchData(CE9178">'/api/user');
// user is CE9178">'any', no autocomplete, no safety.

The fix isn't just about adding types; it's about making the caller responsible for the shape. Here is the refactored version:

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

interface User { id: number; name: string; }

const user = await fetchData<User>(CE9178">'/api/user');
// Now CE9178">'user' is typed as User.

This change took about two minutes to implement but saved me roughly 1.8 hours of debugging per week during our last sprint. The compiler now knows exactly what user is, and if I try to access user.email when it doesn't exist, the build fails immediately.

When Generics in TypeScript Go Too Far

I’ve seen developers fall into the "Generic Hell" trap. This happens when you start nesting generics: Promise<Result<User, Error<string>>>. It’s tempting to try and model every single edge case in the type system, but you end up with code that’s impossible to read or refactor.

I once spent about two days trying to create a "perfect" generic factory pattern for our form components. I wanted the type system to infer the form fields, the validation schema, and the submission result simultaneously. It worked, but if someone made a typo in the schema, the error message was 40 lines long and pointed to a line of code I didn't even write.

My rule of thumb now: If you need more than two generic parameters, or if your type definition spans more than five lines, you’re likely over-engineering. Just write a specific interface or use a union type.

Practical Use Case: API Response Wrappers

Crop anonymous female in casual clothes sorting out rubbish while standing in light apartment in daytime

Most APIs return data wrapped in some metadata. You can use generics to strip that away while keeping your payload safe.

TYPESCRIPT
interface ApiResponse<T> {
  data: T;
  meta: {
    total: number;
    page: number;
  };
}

// Usage
const response = await fetchData<ApiResponse<User[]>>(CE9178">'/api/users');
console.log(response.data[0].name); // Fully typed

This pattern is incredibly common in production codebases. It keeps your API client clean while ensuring that even if the API structure changes, you only have to update the ApiResponse wrapper once. It’s significantly cleaner than the alternative, which is often manually mapping objects or leaving them as any and hoping for the best.

FAQ: Common Generic Questions

Q: Should I use any instead of a generic if I'm in a rush? A: Never. If you're in a rush, use unknown instead. It forces you to perform a type check before using the data, which is much safer than any and usually takes seconds to implement.

Q: Do generics increase my bundle size? A: No. TypeScript generics are erased at compile time. They exist only for the compiler's sanity; they don't add a single byte to your production JavaScript.

Q: When should I use extends in a generic? A: Use it to constrain what can be passed in. For example, <T extends { id: string }> ensures that whatever object you pass must have an id property. It’s a great way to catch errors before they bubble up.

Still Learning

Colorful art piece featuring a sculpture figure reading a book in front of vibrant abstract waves.

I’m still refining how I use generics in complex state management files. Sometimes, I find that a simple interface is better than a clever generic. If you’re just starting, don't feel pressured to use them everywhere. Start with your API layer, see how it feels, and build from there. I’m curious to see how the new TypeScript versions handle type inference in deeply nested objects, as that’s usually where I still run into the most friction. Don't overthink it—just keep the compiler happy and your team safe.

Back to Blog

Similar Posts

Detailed view of code and file structure in a software development environment.
TypeScriptJavaScriptJune 20, 20264 min read

TypeScript narrowing: How to make the compiler trust your code

TypeScript narrowing is the key to writing type-safe code without constant casting. Learn how to guide the compiler through your logic for cleaner builds.

Read more
Typewritten note with 'I love you' on vintage paper background, evoking nostalgia.
TypeScriptJavaScriptJune 20, 20264 min read

TypeScript utility types you will reach for weekly

TypeScript utility types can save you hours of boilerplate code. Learn the essential tools I use weekly to keep my production projects clean and type-safe.

Read more
Evening train at Denver Union Station with iconic sign and transit information display.
TypeScriptJavaScriptJune 20, 20264 min read

Discriminated unions in TypeScript: Modeling state without bugs

Discriminated unions in TypeScript help you model complex state without bugs. Stop using loose objects and start writing type-safe, predictable code today.

Read more