Generic Utilities

Build Your Own Type-Safe Helpers

Generic Utilities

Once you're comfortable with generics, you can write small, reusable utilities that keep their input types.

4 min read Level 2/5 #typescript#generics#utilities
What you'll learn
  • Write small generic helpers
  • Preserve input types through transformations
  • Recognize common patterns

A handful of small generic helpers come up everywhere.

firstOrThrow

function firstOrThrow<T>(arr: T[], msg = "Empty array"): T {
  if (arr.length === 0) throw new Error(msg);
  return arr[0];
}

const u = firstOrThrow([{ id: "1" }, { id: "2" }]);   // { id: string }

groupBy

function groupBy<T, K extends string | number>(
  arr: T[],
  fn: (item: T) => K
): Record<K, T[]> {
  return arr.reduce((acc, item) => {
    const key = fn(item);
    (acc[key] ||= []).push(item);
    return acc;
  }, {} as Record<K, T[]>);
}

const users = [{ role: "admin" }, { role: "user" }, { role: "admin" }];
const byRole = groupBy(users, u => u.role);
// Record<"admin" | "user", typeof users[number]>

uniqueBy

function uniqueBy<T, K>(arr: T[], fn: (item: T) => K): T[] {
  const seen = new Set<K>();
  return arr.filter(item => {
    const k = fn(item);
    if (seen.has(k)) return false;
    seen.add(k);
    return true;
  });
}

uniqueBy(users, u => u.id);

keys That Preserves Literal Types

function keys<T extends object>(obj: T): (keyof T)[] {
  return Object.keys(obj) as (keyof T)[];
}

const user = { name: "Ada", age: 36 };
const ks = keys(user);   // ("name" | "age")[]

Object.keys returns string[] natively. The generic version preserves the actual key set.

pick (Like Lodash)

function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
  const result = {} as Pick<T, K>;
  for (const k of keys) result[k] = obj[k];
  return result;
}

const u = { id: "1", name: "Ada", age: 36 };
const named = pick(u, ["name", "age"]);   // { name: string; age: number }

We cover Pick in the utility types lesson.

The Pattern

Most useful generic utilities take a T input and produce a SomethingOf<T> output — preserving type info through the transformation.

Up Next

Functions with multiple type parameters.

Multiple Type Parameters →