100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
TypeScript

Generics in TypeScript

Learn how TypeScript generics let you write reusable, type-safe code that works across many types without losing type information.

GenericsIntermediate9 min readJul 8, 2026
Analogies

1. Introduction

Generics are TypeScript's way of writing components, functions, and classes that work with a variety of types while still preserving type information. Instead of hard-coding a specific type or falling back to any (which throws away type safety), you introduce a type parameter that acts as a placeholder for whatever type is actually used when the code is called. The compiler then fills in that placeholder and checks everything consistently.

🏏

Cricket analogy: Instead of hardcoding a selectPlayer function to only accept Batter objects, or falling back to any and losing safety, a type parameter T acts as a placeholder that the compiler fills in with Batter, Bowler, or Allrounder depending on the call.

You already use generics whenever you write Array<number>, Promise<string>, or Map<string, number>. Generics are the mechanism that lets these built-in types stay flexible without becoming unsafe.

🏏

Cricket analogy: Every time you write Array<number> to store a batting order's scores, you're already using generics — the same mechanism that keeps TypeScript's built-in structures flexible enough to also model Array<string> for a bowling lineup, without losing type safety.

2. Syntax

typescript
// Generic type parameter declared with angle brackets
function identity<T>(value: T): T {
  return value;
}

// Generic interface
interface Box<T> {
  contents: T;
}

// Generic type alias
type Pair<A, B> = [A, B];

// Explicit type argument vs inferred type argument
const a = identity<string>("hello"); // explicit
const b = identity(42);              // inferred as number

3. Explanation

Generics let you write reusable code that works over a variety of types while preserving type information. Compare function identity(value: any): any with function identity<T>(value: T): T. The any version compiles, but the caller gets no guarantee that the return type matches the input type — TypeScript simply stops checking. The generic version keeps the relationship between input and output types intact: pass in a string, get a string back, with full autocomplete and type-checking preserved.

🏏

Cricket analogy: Compare function tagPlayer(value: any): any with function tagPlayer<T>(value: T): T — the any version compiles but gives no guarantee Kohli's Batter object comes back as a Batter, while the generic version keeps that input-output relationship intact with full autocomplete.

By convention, generic type parameters are named with short, single uppercase letters: T (Type) is the most common single parameter, K (Key) and V (Value) are used for maps/records, and U is used for a second, unrelated type parameter (e.g. when mapping T to U). These names are just a convention — you can use descriptive names like TItem in complex APIs — but single-letter names are idiomatic for simple, generic utility code.

🏏

Cricket analogy: In a generic Scoreboard<T, K, V>, T might be the match type, K the stat name, and V its numeric value, following the T/K/V convention; a complex cricket-analytics API might instead use a descriptive name like TPlayer for clarity.

Type parameters can be constrained with extends (covered in depth in Generic Constraints), combined with keyof for safe property access, used to parametrize classes (Generic Classes), and are the foundation of the built-in utility types like Partial<T> and Pick<T, K>, which transform an existing type rather than requiring you to write a new type from scratch.

🏏

Cricket analogy: Partial<Player> lets you build an incomplete player-transfer form with only some fields filled in, and Pick<Player, "name" | "average"> extracts just those two fields — both utility types are built from the same generic and keyof machinery covered in Generic Constraints and Generic Classes.

Gotcha: T is not a runtime value. Generic type parameters exist only during type-checking and are erased when TypeScript compiles to JavaScript — you cannot do if (T === string) or new T() inside a generic function, because by the time the code runs, there is no T left at all.

4. Example

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

interface Box<T> {
  contents: T;
}

function createBox<T>(contents: T): Box<T> {
  return { contents };
}

const numberIdentity = identity<number>(100);
const stringIdentity = identity("typescript"); // T inferred as string

const numberBox = createBox(42);
const stringBox = createBox("hello generics");

console.log(numberIdentity, stringIdentity);
console.log(numberBox.contents, typeof numberBox.contents);
console.log(stringBox.contents, typeof stringBox.contents);

// This would be a compile-time error (uncomment to see):
// const bad: Box<number> = createBox("not a number");

5. Output

text
100 typescript
42 number
hello generics string

6. Key Takeaways

Practice what you learned

Was this page helpful?

Topics covered

#TypeScript#TypeScriptProgrammingStudyNotes#Programming#GenericsInTypeScript#Generics#Syntax#Explanation#Example#StudyNotes#SkillVeris