EasyGenerics📖 Theory Question

How do you constrain generic functions to work only with specific shapes using interfaces?

💡

Hint

Extend from an interface or inline shape — ensures the generic value has the properties you need

Full Answer

Combining generics with interface constraints lets you write functions that accept any compatible type while retaining full type information.

// Constraint via interface
interface HasId {
  id: number | string;
}

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

// Works with any object that has an id field
const user = findById(users, 1);     // T = User
const post = findById(posts, 'abc'); // T = Post
// Return type is User | undefined and Post | undefined respectively
// Multiple interface constraints (intersection)
interface Serializable { serialize(): string }
interface Comparable<T> { compareTo(other: T): number }

function sortAndSerialize<T extends Serializable & Comparable<T>>(items: T[]): string[] {
  return items
    .sort((a, b) => a.compareTo(b))
    .map(item => item.serialize());
}

// Constraint + keyof for safe property access
function pluck<T, K extends keyof T>(items: T[], key: K): T[K][] {
  return items.map(item => item[key]);
}

const names = pluck(users, 'name'); // string[]
const ids = pluck(users, 'id');     // number[]
💡 T extends HasId is more flexible than accepting HasId[] directly — it preserves the full type of the items, so the return type is T | undefined not just HasId | undefined.

More Generics Questions

EasyWhat are generics and why are they useful in TypeScript?→EasyWhat are generic constraints and how do you use the extends keyword with them?→EasyWhat are default type parameters in TypeScript generics?→EasyWhat is conditional generic typing — how do you write T extends U ? X : Y?→

Practice this in a timed sprint →

5 free questions, no signup required

⚡ Start Sprint