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.
- 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
| Utility | Result |
|---|---|
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
| Utility | Result |
|---|---|
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
| Utility | Result |
|---|---|
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.