back to blog

typescript utility types you'll actually use

typescript has like 20 utility types. you'll use maybe 5 regularly. here they are.

partial

makes everything optional. perfect for update operations.

interface User {
  id: string;
  name: string;
  email: string;
}

// for updates where anything can change
type UpdateUser = Partial<User>;

function updateUser(id: string, data: UpdateUser) {
  // data.name might be undefined
  // data.email might be undefined
  // that's fine
}

use it: update endpoints, patch operations, optional configs.

pick

grab only the fields you need.

interface User {
  id: string;
  name: string;
  email: string;
  password: string;
  createdAt: Date;
}

// api response without sensitive stuff
type PublicUser = Pick<User, 'id' | 'name' | 'email'>;

use it: api responses, dtos, anywhere you need a subset.

omit

opposite of pick. remove fields.

// same result, different approach
type PublicUser = Omit<User, 'password' | 'createdAt'>;

// for creating users (no id yet)
type CreateUser = Omit<User, 'id' | 'createdAt'>;

use it: removing sensitive fields, create operations.

record

object with known key types.

// simple cache
type Cache = Record<string, unknown>;

// permissions
type Permissions = Record<'read' | 'write' | 'delete', boolean>;

// grouped data
type UsersByRole = Record<'admin' | 'user', User[]>;

use it: caches, maps, grouped data, dictionaries.

returntype

get the return type of a function.

async function getUser(id: string) {
  return db.users.findUnique({ where: { id } });
}

// extract the type without redeclaring
type User = Awaited<ReturnType<typeof getUser>>;

use it: when you don't control the function but need its type.

combining them

the real power is combining:

interface User {
  id: string;
  name: string;
  email: string;
  password: string;
  createdAt: Date;
}

// create: no id, no createdAt
type CreateUser = Omit<User, 'id' | 'createdAt'>;

// update: everything optional except id
type UpdateUser = Partial<Omit<User, 'id'>>;

// response: no password
type UserResponse = Omit<User, 'password'>;

one source of truth. change User, everything updates.

that's it

partial, pick, omit, record, returntype.

there are more utility types. you probably don't need them. these five cover most real-world cases.

don't overcomplicate types. they should help you, not slow you down.