Skip to content
intermediatePhase 35 · TypeScript

Generics

Write reusable, type-safe code with generic functions, classes, and constraints.

1h
0 problems
Topic Progress0%

Generic Functions

Generic Functions

Generics allow you to write reusable code that works with multiple types while maintaining type safety.

Basic Generic Function

// Without generics - loses type info
function identity(value: any): any {
  return value;
}

// With generics - preserves type
function identity<T>(value: T): T {
  return value;
}

// Usage
const num = identity<number>(42); // type: number
const str = identity<string>("hello"); // type: string

// Type inference
const inferred = identity(42); // TypeScript infers number

Multiple Type Parameters

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

const result = pair("hello", 42); // [string, number]

Generic Arrow Functions

const getFirst = <T>(arr: T[]): T | undefined => arr[0];

const first = getFirst([1, 2, 3]); // number | undefined
const firstStr = getFirst(["a", "b"]); // string | undefined

Generic with Default

function createArray<T = string>(length: number, fill: T): T[] {
  return Array(length).fill(fill);
}

const strings = createArray(3, "hello"); // string[]
const numbers = createArray<number>(3, 42); // number[]

Generic Classes

Generic Classes

Generic classes allow you to create classes that work with different types.

Basic Generic Class

class Container<T> {
  private value: T;

  constructor(value: T) {
    this.value = value;
  }

  getValue(): T {
    return this.value;
  }

  setValue(value: T): void {
    this.value = value;
  }
}

const numContainer = new Container<number>(42);
const strContainer = new Container<string>("hello");

Generic Stack

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

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

  pop(): T | undefined {
    return this.items.pop();
  }

  peek(): T | undefined {
    return this.items[this.items.length - 1];
  }

  get size(): number {
    return this.items.length;
  }
}

const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);

Generic Interface

interface Repository<T> {
  findById(id: number): T | undefined;
  findAll(): T[];
  create(item: T): T;
  update(id: number, item: Partial<T>): T;
  delete(id: number): void;
}

class UserRepository implements Repository<User> {
  private users: User[] = [];

  findById(id: number) {
    return this.users.find(u => u.id === id);
  }

  findAll() {
    return this.users;
  }

  create(user: User) {
    this.users.push(user);
    return user;
  }

  update(id, updates) {
    const user = this.findById(id);
    if (user) Object.assign(user, updates);
    return user!;
  }

  delete(id) {
    this.users = this.users.filter(u => u.id !== id);
  }
}

Generic Constraints

Generic Constraints

Constraints limit which types can be used with generics.

Using extends

// T must have a length property
function logLength<T extends { length: number }>(arg: T): T {
  console.log(arg.length);
  return arg;
}

logLength("hello"); // OK
logLength([1, 2, 3]); // OK
// logLength(42); // Error! number has no length

keyof Constraint

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

const user = { name: "Alice", age: 30 };

getProperty(user, "name"); // OK
getProperty(user, "age"); // OK
// getProperty(user, "email"); // Error!

Class Constraints

interface HasId {
  id: number;
}

function findById<T extends HasId>(items: T[], id: number): T | undefined {
  return items.find(item => item.id === id);
}

Generic Utility with Constraints

function merge<T extends object, U extends object>(obj1: T, obj2: U): T & U {
  return { ...obj1, ...obj2 };
}

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

Practice Problems

0/3solved
Build Generics Component

Create a reusable React component implementing Generics. Include proper state management and accessibility.

Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where needed
Generics Testing

Write unit and integration tests for Generics using React Testing Library.

Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility tests
Generics Performance

Optimize Generics for performance. Consider memoization, code splitting, and bundle size.

Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysis

Quiz

1. What does `<T>` represent in a generic function?

Question 1 options

2. How do you constrain a generic type?

Question 2 options

3. What does `keyof` do in a generic constraint?

Question 3 options

4. Can generic classes have multiple type parameters?

Question 4 options

Flashcards

Question

What is a generic type parameter?

Answer

A placeholder type that can be replaced with any type

Question

How do you constrain generics?

Answer

Use extends: <T extends Constraint>

Question

What is keyof?

Answer

An operator that creates a union of property names

Question

Can generics have default types?

Answer

Yes: <T = string>

Question

What is Generics?

Answer

Generics is a key concept in frontend development.

Revision Notes

Key Takeaways

  • 1.Generics provide type safety while maintaining flexibility
  • 2.Type parameters (T, U) can be replaced with any type
  • 3.Use extends to constrain generic types
  • 4.keyof creates a union of property names
  • 5.TypeScript infers generic types when possible

Interview Tips

  • Explain why generics are better than any
  • Show examples of generic functions and classes
  • Demonstrate understanding of generic constraints

Cheat Sheet

Cheat Sheet

Generic Function

function identity<T>(value: T): T {
  return value;
}
const num = identity(42);

Generic Class

class Box<T> {
  value: T;
  constructor(v: T) { this.value = v; }
}

Constraints

function logLength<T extends { length: number }>(arg: T) {
  console.log(arg.length);
}

keyof

function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}