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