The Code Dealer Logo
All cheat sheets

TypeScript Utility Types Cheat Sheet

Every built-in utility type worth memorising, what it actually does to a type, and the one-line example that makes it stick.

The Code DealerUpdated
  • typescript
  • cheatsheet
  • types

Utility types let you derive one type from another instead of maintaining two by hand. The rule behind all of them: one source of truth, everything else computed. When your API response type changes, every derived type changes with it.

Assume this base type throughout:

type Course = {
  id: string
  title: string
  price: number
  publishedAt: Date
  authorId: string
}

Reshaping object types

UtilityResult
Partial<Course>every property optional
Required<Course>every property required
Readonly<Course>every property readonly
Pick<Course, 'id' | 'title'>only the listed keys
Omit<Course, 'authorId'>everything except the listed keys
Record<string, Course>an object keyed by string

Partial is the update payload. Omit is the create payload. Writing them out by hand is how the two drift apart:

type CourseUpdate = Partial<Omit<Course, 'id'>>
type CourseCreate = Omit<Course, 'id' | 'publishedAt'>

Pick vs Omit

They are the same operation from opposite ends, and the choice matters for maintenance. Pick is a closed list — adding a field to Course does not add it to the derived type. Omit is open — a new field appears automatically.

Use Pick for anything crossing a boundary you control, like a public API response, so a new column is never accidentally exposed. Use Omit for internal shapes where you want new fields to flow through.

Narrowing unions

UtilityResult
Exclude<T, U>members of T not assignable to U
Extract<T, U>members of T assignable to U
NonNullable<T>T without null and undefined
type Status = 'draft' | 'published' | 'archived'
type Visible = Exclude<Status, 'archived'> // 'draft' | 'published'

Exclude and Extract operate on union members, not object keys. Reach for Omit when you are removing a property and Exclude when you are removing a variant — mixing them up is the most common error here.

Reading types off functions

UtilityResult
ReturnType<typeof fn>what fn returns
Parameters<typeof fn>its arguments, as a tuple
Awaited<T>T with promises unwrapped

These stop you from exporting a type just so a caller can name it:

async function getCourse(id: string) {
  return prisma.course.findUnique({ where: { id } })
}
 
type CourseResult = Awaited<ReturnType<typeof getCourse>>

CourseResult now tracks the Prisma query exactly — including the null for a missing row, and any relation you add via include later.

Awaited recursively unwraps, so a promise of a promise still resolves to the final value. Note that ReturnType on an async function gives you the promise; you almost always want it wrapped in Awaited.

String literal manipulation

Uppercase, Lowercase, Capitalize and Uncapitalize transform string literal types, which is how typed event names stay in sync:

type Event = 'created' | 'updated'
type Handler = `on${Capitalize<Event>}` // 'onCreated' | 'onUpdated'

Two things to watch

Omit does not check its keys. Omit<Course, 'titel'> compiles and silently omits nothing. Pick does check, which is another reason to prefer it where either works.

Utility types are structural, not nominal. Omit on a class gives you a plain object type — the methods survive as properties, but the result is no longer an instance of that class.

Learn this set and you will rarely need to hand-write an object type twice.