Generics

Loading concept...

TypeScript Generics: The Magic Box That Fits Everything! 🎁

The Story of the Magic Container

Imagine you have a magic box. This box is special because it can hold anything you put inside it—toys, cookies, books, or even your pet goldfish! But here’s the cool part: once you tell the box what it’s holding, it remembers and only gives you back that exact type of thing.

That’s what Generics are in TypeScript. They’re like magic containers that work with any type you choose, while keeping everything safe and organized.


🎯 Generic Functions

The Universal Helper

A generic function is like a helpful friend who can do the same task for any type of thing you give them.

Simple Example:

function getFirst<T>(items: T[]): T {
  return items[0];
}

// Works with numbers!
const firstNum = getFirst([1, 2, 3]);
// Result: 1

// Works with strings too!
const firstWord = getFirst(["apple", "banana"]);
// Result: "apple"

What’s happening?

  • The <T> is like a placeholder label
  • When you call the function, TypeScript fills in T with the actual type
  • The function works the same way, no matter what type you use!
graph TD A["getFirst&lt;T&gt;"] --> B["T = number"] A --> C["T = string"] A --> D["T = any type!"] B --> E["Returns: number"] C --> F["Returns: string"] D --> G["Returns: that type"]

🏗️ Generic Interfaces

The Blueprint That Adapts

An interface is like a blueprint for building things. A generic interface is a blueprint that works for different materials.

Simple Example:

interface Box<T> {
  content: T;
  label: string;
}

// A box for toys
const toyBox: Box<string> = {
  content: "Teddy Bear",
  label: "My Toys"
};

// A box for numbers
const scoreBox: Box<number> = {
  content: 100,
  label: "High Score"
};

Real Life:

  • Think of a shipping box label
  • The label format stays the same
  • But the contents can be anything!

🏛️ Generic Classes

The Factory That Makes Anything

A generic class is like a factory machine that can produce containers for any product.

Simple Example:

class Storage<T> {
  private items: T[] = [];

  add(item: T): void {
    this.items.push(item);
  }

  getAll(): T[] {
    return this.items;
  }
}

// Storage for fruits
const fruitStorage = new Storage<string>();
fruitStorage.add("Apple");
fruitStorage.add("Banana");

// Storage for numbers
const numStorage = new Storage<number>();
numStorage.add(42);
numStorage.add(100);

Why is this cool?

  • One class definition
  • Works for strings, numbers, objects—anything!
  • TypeScript checks that you only add the right type

🔒 Generic Constraints

Setting Some Rules

Sometimes your magic box needs rules. You can’t put a whale in a small box, right?

Constraints let you say: “This generic only works with types that have certain features.”

Simple Example:

interface HasLength {
  length: number;
}

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

// Works! Strings have length
logLength("Hello");  // Output: 5

// Works! Arrays have length
logLength([1, 2, 3]);  // Output: 3

// Error! Numbers don't have length
// logLength(42);  ❌

The extends keyword says: “T must have a length property!”

graph TD A["T extends HasLength"] --> B{"Does T have length?"} B -->|Yes| C["✅ Allowed"] B -->|No| D["❌ Error"]

🎭 Multiple Type Parameters

More Than One Placeholder

What if your magic box needs to hold two different things? Use multiple type parameters!

Simple Example:

function makePair<A, B>(first: A, second: B): [A, B] {
  return [first, second];
}

// String and number pair
const pair1 = makePair("age", 25);
// Result: ["age", 25]

// Boolean and string pair
const pair2 = makePair(true, "yes");
// Result: [true, "yes"]

Naming Convention:

  • T = Type (most common)
  • K = Key
  • V = Value
  • A, B, C = when you need multiple

🎁 Generic Defaults

The Backup Plan

Sometimes you want a default type if no one specifies one. Like a vending machine that gives you cola if you don’t choose.

Simple Example:

interface Response<T = string> {
  data: T;
  status: number;
}

// Uses default (string)
const res1: Response = {
  data: "Hello!",
  status: 200
};

// Override with number
const res2: Response<number> = {
  data: 42,
  status: 200
};

The = string part means: “If you don’t tell me the type, I’ll use string!”


🔮 Generic Inference

TypeScript’s Smart Guessing

Here’s the magic part: TypeScript is really smart. It can figure out the type without you telling it!

Simple Example:

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

// No need to write wrap<string>(...)
const result = wrap("hello");
// TypeScript knows: result is { wrapped: string }

const numResult = wrap(123);
// TypeScript knows: numResult is { wrapped: number }

How does it work?

  1. You call wrap("hello")
  2. TypeScript sees "hello" is a string
  3. It automatically sets T = string
  4. No extra typing needed!
graph TD A["wrap#40;42#41;"] --> B["TypeScript sees: 42"] B --> C["Infers: T = number"] C --> D["Returns: { wrapped: number }"]

🔐 Const Type Parameters

Locking In the Exact Value

With const type parameters, TypeScript remembers the exact value, not just the general type.

Simple Example:

// Without const - generic type
function getItems<T extends readonly string[]>(
  items: T
) {
  return items;
}

const fruits = getItems(["apple", "banana"]);
// Type: string[] (general)

// With const - exact values locked!
function getExactItems<const T extends readonly string[]>(
  items: T
) {
  return items;
}

const exactFruits = getExactItems(["apple", "banana"]);
// Type: readonly ["apple", "banana"] (exact!)

Why use const?

  • Normal: TypeScript says “it’s an array of strings”
  • With const: TypeScript says “it’s EXACTLY these values in this order!”

Real Use Case:

function createMenu<const T extends readonly string[]>(
  options: T
): T[number] {
  return options[0];
}

const menu = createMenu(["Home", "About", "Contact"]);
// menu can ONLY be "Home" | "About" | "Contact"
// Not just any string!

🎯 Putting It All Together

Here’s a complete example using everything we learned:

// Generic interface with constraint
interface Identifiable {
  id: number;
}

// Generic class with constraint + default
class DataStore<T extends Identifiable = { id: number }> {
  private items: T[] = [];

  // Generic method with inference
  add(item: T): T {
    this.items.push(item);
    return item;
  }

  // Multiple type parameters
  findAndTransform<R>(
    id: number,
    transform: (item: T) => R
  ): R | undefined {
    const item = this.items.find(i => i.id === id);
    return item ? transform(item) : undefined;
  }
}

// Usage
interface User extends Identifiable {
  name: string;
}

const userStore = new DataStore<User>();
userStore.add({ id: 1, name: "Alice" });

// TypeScript infers everything!
const greeting = userStore.findAndTransform(
  1,
  user => `Hello, ${user.name}!`
);
// greeting is: string | undefined

🌟 Quick Summary

Concept What It Does Example
Generic Functions Work with any type function get<T>(x: T): T
Generic Interfaces Flexible blueprints interface Box<T>
Generic Classes Reusable containers class Store<T>
Constraints Set type requirements T extends SomeType
Multiple Params Handle multiple types <A, B, C>
Defaults Fallback type <T = string>
Inference Smart type guessing Automatic!
Const Params Lock exact values <const T>

🎉 You Did It!

Generics might seem tricky at first, but remember:

Generics are just placeholders that TypeScript fills in for you.

Like a fill-in-the-blank game where TypeScript is really good at guessing the right answer!

Now go build something amazing with your new superpower! 🚀

Loading story...

No Story Available

This concept doesn't have a story yet.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.

Interactive Preview

Interactive - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.

No Interactive Content

This concept doesn't have interactive content yet.

Cheatsheet Preview

Cheatsheet - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.

No Cheatsheet Available

This concept doesn't have a cheatsheet yet.

Quiz Preview

Quiz - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.

No Quiz Available

This concept doesn't have a quiz yet.