Built-in utility types
A utility type takes a type you already have and returns a new, related type. TypeScript ships a standard set of them, so you do not write the new type by hand. This chapter is about using them. Chapter 07 shows how they are built.
Why utility types exist: one Order, five variants
The same tea shop. Order is the central type of the system, and real code needs several types that are almost, but not quite, Order.
Five receipts, five types
A draft order: the customer is still choosing, so properties may be missing. A locked order: the receipt is printed and nothing may change. A list row: three properties are shown. A public API response: the internal note must not leave the building. A stock table: one number per cup size. Five requirements, five types.
You could write out all five by hand from Order. Six properties, copied five times. Then Order gains a property, and five places have to be edited. Miss one and the types quietly disagree with each other.
TypeScript solves this with a set of utility types that ship with the language. Each one takes an existing type and produces a new one. The syntax looks like a function call, with angle brackets instead of parentheses: Partial<Order>. That is the same syntax as the generics in Chapter 05, and for the same reason: the type inside the brackets is an argument. One utility type covers each of the five requirements above.
The customer has not finished choosing, so any property may be left out. Six ? markers, added at once.
After the receipt is printed, assigning to a property is a compile error. Six readonly markers, added at once.
A row shows the order number, the drink, and the cup size. An allow-list: named properties stay, the rest are left out.
internalNote is for staff only. A block-list: named properties are removed, the rest stay as they are.
A utility type transforms; it never modifies
A utility type does not invent anything new. It reads an existing type and returns a new one. It also never changes its input: after Partial<Order> produces a draft type, Order still has the same six required properties. This holds for every utility type in the chapter, and the pipeline below shows it directly.
Get the feel first: a type-processing line
Before the API list, watch the six properties of Order go through four different tools. Every later section is then a reminder of something you have already seen.
Three things to notice. A type goes in and a type comes out; nothing runs. Switching the tool changes only the output, never the input. And a property that Pick or Omit leaves out is missing from the new type only. It is still in Order, and any code that uses Order is unaffected.
Change the shape: Partial, Required, Readonly
The first group adds and removes no properties. It changes how each existing property behaves: optional or required, writable or read-only.
Partial<T> · every property optional
? to a property by hand. Partial does that to every property at once. Read the last line carefully: because the property may be absent, reading it gives string | undefined, so you have to narrow it before use. Chapter 03 covers narrowing.Required<T> · every property required
Partial and Required are opposites for the optional marker, but they are not exact inverses of each other. Partial adds ?, which adds undefined to the property type; Required removes both. That is why Required<A> above is { note: string } and not { note: string | undefined }.Readonly<T> · every property read-only
readonly rejects assignment to the property. It says nothing about the object or array the property points at, so push is still allowed.This whole group is shallow
Partial, Required and Readonly only touch the properties of the top-level object. They never look inside a property type. In Partial<{ meta: { note: string } }>, meta becomes optional and meta.note stays required.
For Readonly this is worth stating twice, because the name suggests more than it does. It prevents assignment to the property. It does not freeze the object the property points at, and nothing about it exists at run time: Object.freeze is a separate, runtime thing.
There is no built-in deep version of any of these. All the parts you need are in Chapter 07, and DeepReadonly is one of the things you will write there.
Choose properties: Pick, Omit, Record
The second group decides which properties exist: an allow-list, a block-list, and Record, which builds an object type from a set of keys and one value type.
Pick / Omit · allow-list and block-list
Which one to use? Whichever list is shorter. A list row needs three of six properties, so Pick is shorter. A public response removes one of six, so Omit is shorter. Both copy the optional and readonly markers of each property they keep.
Pick checks its keys. Omit does not.
Start with code that looks fine:
The reason is in the two definitions. Pick<T, K> declares K extends keyof T, so K has to be a real key of T. Omit<T, K> declares K extends keyof any, which accepts any string, number or symbol. The wider constraint is deliberate: it lets you write Omit for a key that may or may not be present. The cost is that a typo is not an error.
This matters most in exactly the case where Omit is most tempting. If you remove a property because it must not be sent to a client, a misspelled key compiles and the property is still there. Use a Pick allow-list for that, so the compiler checks the names, or write a strict Omit of your own after Chapter 07.
Record<K, V> · build an object type from keys and a value type
Record<Size, number> is stricter than { [k: string]: number } because Size is a finite union, so the compiler knows exactly which keys must exist. With string as the key type, Record becomes an index signature and there is nothing to check. Read the last line: an index signature promises a value for every key, so reading a missing one type-checks and returns undefined at run time. Turn on noUncheckedIndexedAccess (Chapter 10) to make the compiler add | undefined there.Filter unions: Exclude, Extract, NonNullable
The tools so far worked on object properties. This group works on the members of a union type, which brings back the order status from Chapter 03.
"done" and "cancelled" are dropped, and the rest stay. This is the list of orders that are still in progress, so it is the right type for the pickup screen.Properties or members? Ask before you reach for a tool
Partial, Pick and Omit take an object type and work on its properties. Exclude, Extract and NonNullable take a union type and work on its members. When you are not sure which you need, ask: is this type a record of properties, or a list of alternatives?
All three of this group are applied to each member of the union separately, which is why Exclude<A | B | C, B> gives A | C instead of comparing the whole union at once. Chapter 07 shows the one-line definition that produces this behavior.
From functions and promises: Parameters, ReturnType, Awaited
The third group reads a type out of a function type: what it takes, what it returns, and what an async result contains. This is most useful when you did not write the type yourself.
typeof makeOrder. makeOrder is a value, and ReturnType needs a type. In a type position, typeof takes the type of a value, which is the bridge between the two. Leaving it out is the most common mistake here, and the last three lines show the exact error. This typeof shares its name with the JavaScript operator but does something different; Chapter 07 covers it.When is this useful? When a library exports a function but not the type of its result, and when you do not want to give an internal function's result a name of its own. Note that Parameters<T> returns a tuple, not a union or an object, so MakeOrderArgs[1] is Size and the tuple can be spread straight into a call.
Awaited removes every layer of Promise, not just one, which matches what a chain of await does at run time. Over a union it is applied to each member, so Awaited<Promise<string> | number> is string | number.Also in the set: four string types, and one rare one
Uppercase<"large"> is "LARGE" and Lowercase goes the other way. Capitalize<"size"> is "Size" and Uncapitalize goes the other way. All four work on string literal types only, and they become genuinely useful together with the template literal types in Chapter 07. For now, just recognize the names.
There is also NoInfer<T>, added in TypeScript 5.4. It marks one parameter position so the compiler does not use it when inferring a type argument. It is rare. Knowing it exists is enough.
Compose them: a type out is a type in
Every utility type takes a type and returns a type, so the result of one can be the argument of the next. Most real project types are built this way.
Record.Does the order matter? Sometimes. Two tools that work on the same level often commute, and two tools that work on different levels usually do not. Rather than memorising rules, check the result by hovering the alias:
Partial and Pick both act on the properties of the same object, so swapping them changes nothing. Partial<Record<…>> makes the keys optional, while Record<…, Partial<…>> keeps all keys required and makes each value incomplete. Those are two different types, and the last error shows it.Three common mistakes
One: a utility type never changes its input. However deeply you nest them, Order is still Order. Each step produces a new type.
Two: Partial, Required and Readonly are shallow. Nested property types are untouched, and Readonly blocks assignment to the property rather than mutation of the object it points at. §03 covers this.
Three: a misspelled key in Omit is not an error. When you are removing a property for safety, check the spelling, or use a Pick allow-list so the compiler checks it for you.
Next chapter: how these are built
These tools are useful, and none of them is special. Partial is defined in one line of TypeScript, and Exclude is shorter. Chapter 07 takes them apart: mapped types, conditional types, keyof, infer. There are only a few parts, and by the end you can write every tool from this chapter yourself.
Labs
Four tasks, all of which run in the TypeScript Playground: compose three Order variants, see Omit accept a misspelled key, compare Record with an index signature, and unwrap nested promises.
Quiz
Nine questions. The two that are missed most often are about shallowness and about the misspelled key in Omit, and both were covered above.
What exactly does type DraftOrder = Partial<Order> do?
What happens with Omit<Order, "internalNotes">? Note the misspelled key: there is an extra s.
Given const o: Readonly<Order> = …, which line does the compiler reject?
Which of these take a union type and return a union with some members removed or kept? (Select all that apply.)
What is the main difference between Record<Size, number> and { [k: string]: number }?
In ReturnType<typeof makeOrder>, what is typeof doing?
Awaited<Promise<Promise<number>>> resolves to the type ____.
After type T = Partial<{ meta: { note: string } }>, which statement is true?
Given type A = { note?: string }, what is the type of note in Required<A>?
- A utility type takes a type and returns a new type. The syntax looks like a function call with angle brackets, and the input type is never modified.
- Change the shape:
Partialadds?,Requiredremoves it along withundefined, andReadonlyblocks assignment to each property. All three are shallow and only touch the top level. - Choose properties:
Pickis an allow-list,Omitis a block-list, andRecord<K, V>builds an object type and requires every key whenKis a finite union. OnlyPickchecks its keys. - Filter unions:
Excluderemoves matching members,Extractkeeps them, andNonNullableremovesnullandundefined. These work on members, not on properties. - From functions:
Parametersgives a tuple andReturnTypegives the return type. Both needtypeof fn, notfn.Awaitedremoves every layer ofPromise. - They compose, and you read the result from the inside out. What is missing here (a deep
Readonly, a strictOmit) you build yourself in Chapter 07.