f in x
Generics in TypeScript — Write Reusable and Type-Safe Code That Doesn't Break
> cd .. / HUB_EDITORIALE
Sviluppo di siti web

Generics in TypeScript — Write Reusable and Type-Safe Code That Doesn't Break

[2026-06-28] Author: Ing. Calogero Bono
Zenithby Meteora Web Il sistema operativo della tua attività. Social, clienti, prenotazioni e fatture in un'unica piattaforma. Palestre, barber, professionisti. Scopri Zenith Demo gratis · senza carta

You have a function that works with arrays of numbers, then you need one for strings, then for objects. You write three almost identical versions. The code bloats, duplicates, and when you need to change the logic you have to update them all. This is a concrete problem we see often in the Laravel and Vue projects we handle.

We at Meteora Web see it during refactoring: functions using any that lose all type safety, or manual overloads that become a maintenance nightmare. Generics solve exactly this: you write the logic once, leaving the caller free to decide the type, but with the guarantee that TypeScript will check everything.

This guide shows what generics are, how to use them, and how to avoid common mistakes. We start from the problem, not from theory.

What Are Generics in TypeScript and Why Do You Need Them for Reusable Code?

A generic is a type parameter. Instead of fixing a type (e.g. number), you write <T> and use T as a placeholder. When you call the function, TypeScript infers the real type from what you pass.

Basic example without generics:

function firstElement(arr: number[]): number | undefined {
  return arr[0];
}
// If you want for strings, you rewrite:
function firstElementStr(arr: string[]): string | undefined { ... }

With generics:

Sponsored Protocol

function firstElement<T>(arr: T[]): T | undefined {
  return arr[0];
}

const num = firstElement([1,2,3]);      // type number | undefined
const str = firstElement(['a','b']);     // type string | undefined

The type is inferred automatically. We write one function, not three. This is the heart of reusability without sacrificing type safety.

Common mistakes to avoid

The most frequent? Using any instead of a generic. any disables checks; a generic keeps them. Another mistake is forgetting the return type: if the function returns something different from T, TypeScript will warn you.

How to Write and Use a Generic Function in TypeScript?

The syntax is straightforward: after the function name, inside <>, you declare one or more type parameters. You can use them in parameters, return type, and body.

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

// Explicit call
const result = identity<number>(42);
// Or inferred (more common)
const result2 = identity('hello');

You can have multiple generics:

Sponsored Protocol

function merge<A, B>(obj1: A, obj2: B): A & B {
  return { ...obj1, ...obj2 };
}

const merged = merge({ name: 'Alice' }, { age: 30 });
// type: { name: string; } & { age: number; }

Immediate action: Open your TypeScript project, find a function that uses any and replace it with a generic. Try passing different types and see if TypeScript gives inconsistent errors.

How to Constrain Generics with Extends for Better Safety?

Sometimes you don't want to accept any type, only those with certain properties. Use extends to constrain the generic.

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

getLength('hello');      // ok, string has length
getLength([1,2,3]);      // ok
getLength(42);           // error: number has no length

A more useful case: extract a property from an object in a type-safe way.

function getProperty<Obj, Key extends keyof Obj>(obj: Obj, key: Key): Obj[Key] {
  return obj[key];
}

const person = { name: 'Mario', age: 35 };
const name = getProperty(person, 'name'); // type string
const age = getProperty(person, 'age');   // type number
// getProperty(person, 'surname'); // error: 'surname' not in keyof

This is a pattern we use daily in state managers and validation APIs.

Sponsored Protocol

Sharing constraints across multiple generics

function copyFields<T, U extends T>(source: T, target: U): U {
  Object.assign(target, source);
  return target;
}

Ensures that U has at least the same properties as T, without losing extra ones.

When to Use Advanced Generic Types: infer, Conditional Types, Mapped Types?

Generics are not just for functions. You can use them to create conditional types that change depending on the passed type.

type IsString<T> = T extends string ? 'yes' : 'no';
type A = IsString<'hello'>; // 'yes'
type B = IsString<42>;       // 'no'

The infer keyword allows you to extract a type from another complex type:

type ArrayItem<T> = T extends (infer U)[] ? U : never;
type Item = ArrayItem<number[]>; // number

Mapped types use generics to transform an object's properties:

type Readonly<T> = {
  readonly [K in keyof T]: T[K];
};

type Point = { x: number; y: number };
type ReadonlyPoint = Readonly<Point>; // { readonly x: number; readonly y: number }

In practice, you use them when you need custom utility types — for instance, making all properties optional or required in a DTO.

Sponsored Protocol

Action: In your editor, try defining a mapped type that adds a prefix to all keys of an object using keyof and template literal types.

What Common Mistakes Do Developers Make with Generics and How to Avoid Them?

The most subtle mistake: thinking that the generic is evaluated at runtime. It is not. Generics are a compile-time construct. You cannot do typeof T or instanceof T in runtime code. TypeScript erases them after compilation.

function createInstance<T>(ctor: new () => T): T {
  return new ctor();
}
// This works because you pass the constructor at runtime, not T.

// Wrong:
function logType<T>() {
  console.log(typeof T); // error: T is only a type
}

Another mistake is not specifying the generic when TypeScript cannot infer it automatically. In these cases, provide the type explicitly.

function wrapInArray<T>(item: T): T[] {
  return [item];
}

const arr = wrapInArray(5); // ok
const arr2 = wrapInArray<string>(5); // error: 5 is not a string

Remember: a generic is not any with a different name. If your code uses the generic only to pass it around without checks, you are nullifying the advantage.

Sponsored Protocol

What to Do Now

Here are three immediate actions to apply what you've learned:

  1. Find a function in your project that has overloads for two or three different types (e.g. fetchData(url: string): Promise<User> | Promise<Product>). Convert it into a single generic function.
  2. Add an extends constraint to a function that accepts objects with an id: number property, so the caller cannot pass an object without that property.
  3. Read the official section on Generics in the TypeScript Handbook to dive deeper into advanced use cases.

Generics are a powerful tool: they allow you to write flexible code without sacrificing type rigidity. We use them in every project, from Laravel backend with TypeScript to Vue components. If you want to see how we apply them in real-world contexts, check out our comprehensive TypeScript guide.

Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Ingegnere informatico, fondatore di Meteora Web e Zenith OS. System administrator e progettista di piattaforme, app e CMS proprietari, con esperienza in sviluppo full-stack, marketing digitale ed ecosistema Google.
[ Read Full Dossier ]

> METEORA_WEB // DIGITAL AGENCY

We build the digital presence your business deserves.

Websites, social media, online advertising, e-commerce and high-performance hosting, engineered with method by computer engineers in Sciacca, for all of Italy.

> MW_JOURNAL

> READ_ALL()