Expert · TypeScript basics · topic 46 of 61 · 30 min
Utility types and real-world TypeScript
Explanation
Why utility types?
You often need variations of one type: a User, a User without the password for the browser, a User where every field is optional for an update form. Copying the shape three times means three places to update. Utility types derive the variations automatically, so they stay in sync.
interface User { id: number; name: string; email: string; password: string }
type UserUpdate = Partial<User>; // every field optional
type PublicUser = Omit<User, 'password'>; // everything except password
type LoginForm = Pick<User, 'email' | 'password'>;
type FrozenUser = Readonly<User>; // no field can be reassigned
type StockByItem = Record<string, number>; // { [item: string]: number }
type Roles = Record<'admin' | 'staff' | 'guest', boolean>;| Utility | Result |
|---|---|
| Partial<T> | All properties optional |
| Required<T> | All properties required |
| Readonly<T> | All properties readonly |
| Pick<T, K> | Only the properties K |
| Omit<T, K> | All properties except K |
| Record<K, V> | An object with keys K and values V |
| ReturnType<F> | The return type of function type F |
| Awaited<T> | The value inside a Promise |
as const and satisfies
const STATUSES = ['PLACED', 'SHIPPED', 'DELIVERED'] as const;
type Status = (typeof STATUSES)[number]; // 'PLACED' | 'SHIPPED' | 'DELIVERED'
const ROUTES = {
home: '/',
orders: '/orders',
} satisfies Record<string, string>; // checked against the type, but keeps exact keys
ROUTES.orders; // OK, and ROUTES.ordrs is an errorValidating data at the boundary
Types vanish at run time, so JSON from an API or a form can be anything. The professional pattern is: treat it as unknown, check it with code (or a validation library such as Zod), and only then give it a type. Everything inside your program can then trust the types.
interface Order { id: string; amount: number }
function isOrder(x: unknown): x is Order {
return typeof x === 'object' && x !== null
&& typeof (x as Order).id === 'string'
&& typeof (x as Order).amount === 'number';
}
const data: unknown = JSON.parse('{"id":"OD1","amount":499}');
if (isOrder(data)) console.log(data.amount + 1); // 500, fully typedReading TypeScript errors
Long errors look scary, but the useful line is usually the LAST one, which names the exact property that does not match. Read from the bottom up, find the property, then compare the expected and actual types.
Type '{ id: number; name: string; }' is not assignable to type 'User'.
Property 'email' is missing in type '{ id: number; name: string; }' but required in type 'User'.When is any acceptable?
While converting an old JavaScript project step by step, in quick experiments, or for a genuinely dynamic value from a poorly typed library, wrapped and checked quickly. Many teams enable a lint rule that warns on any so it is always a conscious choice.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Utility types and real-world TypeScript · 1/6
In this lesson you will build new types from existing ones and see how TypeScript is used in real projects.
Prefer a video by a teacher? Find free videos on “typescript utility types partial pick omit record satisfies” on YouTube ↗
In real life: An edit-profile form
A job portal lets users edit their profile. The update API accepts any subset of fields except id and email. The team types the request body as Partial<Omit<Profile, 'id' | 'email'>>, so the form cannot accidentally send an id change, and adding a new profile field automatically makes it editable.
interface Profile { id: number; email: string; name: string; city: string; skills: string[] }
type ProfileUpdate = Partial<Omit<Profile, 'id' | 'email'>>;
const change: ProfileUpdate = { city: 'Hyderabad' }; // OK
// const bad: ProfileUpdate = { email: 'x@y.com' }; // Error: email is not allowedPractice (0/3 done)
Write applyUpdate(user: User, update: Partial<User>): User that returns a NEW user with the given fields changed. The original must not change.
Write toPublic(user: User): Omit<User, "password"> that returns the user without the password field.
Write a type guard isOrder(x: unknown): x is Order for interface Order { id: string; amount: number; items: string[] }, and parseOrders(json: string): Order[] that parses a JSON array and keeps only the valid orders.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Basic types and inferenceTypeScript basics · same module
- Generics and utility typesFrontend Web Development · TypeScript basics
- Why TypeScriptFrontend Web Development · TypeScript basics
- TypeScript in real projectsFrontend Web Development · TypeScript basics
- Typing functions and objectsFrontend Web Development · TypeScript basics
- Validating inputBackend Development · APIs and request handling
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
Which type makes every property optional?
Answer: Partial<T>
Partial adds ? to every property.
What is Omit<{ a: 1; b: 2; c: 3 }, 'b'>?
Answer: { a: 1; c: 3 }
Omit removes the listed keys.
Where should outside JSON be validated?
Answer: At the boundary where it enters the program
Types are erased, so check data when it arrives.
const s = ['a', 'b'] as const; What is the type of s?
Answer: readonly ['a', 'b']
as const makes a readonly tuple of the exact literals.
Which utility type makes every property optional, for an "edit profile" form where any field may change?Related
let count = 10; What type does TypeScript infer for count?Related
Answer: number
The value 10 is a number, so TypeScript infers number for a let variable.
From Why TypeScript
When does TypeScript report type errors?Related
Answer: Before running, when you compile or in the editor
Type checking happens at compile time.
From Why TypeScript
const input = document.querySelector<HTMLInputElement>("#pin"); What is the type of input?Related
Answer: HTMLInputElement | null
querySelector returns null when nothing matches, so you must check before using it.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- This is the last topic of “TypeScript basics”. The module exam comes next.
- Module 9 exam: TypeScript basics · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Functional patterns and testing. 61 topics left in the course before the final exam.