Lesson 6-2 introduced Box<T>, an interface with a type slot. Built-in utility types such as Partial<T> are the same idea applied to whole types.
Partial<Settings> produces a type like Settings but with every property optional. It keeps every property of the original and marks each one with ?, the same optional marker from lesson 3-2.
Two points worth fixing in place now. Utility types are generic types that take another type as their argument, so the thing in the angle brackets is a type rather than a value. And they are purely type-level tools, so nothing about them exists at runtime.
Types made from other types
TypeScript ships utility types, generic types that transform existing ones. They exist because real projects constantly need variations of one shape, such as the same user with fields optional for an update form, or with private fields removed for display. Hand-writing each variation means shapes that drift out of sync.
The ones you will use weekly:
| Utility | Meaning |
|---|---|
Partial<T> | every property of T becomes optional |
Pick<T, K> | keep only the listed properties |
Omit<T, K> | keep everything except the listed properties |
Readonly<T> | every property of T becomes readonly |
The classic Partial use case is an update function. Callers change one setting, not all three:
interface Settings { theme: string; fontSize: number; autosave: boolean; } function applyChanges(base: Settings, changes: Partial<Settings>): Settings { return { ...base, ...changes }; }
The spread syntax from Advanced JavaScript merges the objects, and the types guarantee changes only ever contains valid Settings properties.
Readonly<T> applies lesson 3-2's readonly to every property at once, for values that must never be mutated after creation.
Applying a partial update
The changes object below supplies only theme, and everything else comes from the base settings.
interface Settings { theme: string; fontSize: number; autosave: boolean; } const defaults: Settings = { theme: "light", fontSize: 14, autosave: true }; function applyChanges(base: Settings, changes: Partial<Settings>): Settings { return { ...base, ...changes }; } const mine = applyChanges(defaults, { theme: "dark" }); console.log(mine.theme + " " + mine.fontSize + " " + mine.autosave);
Output
dark 14 true
Partial<Settings> means each of the three properties may be present or absent, and no others are allowed. That second half is what makes the type useful rather than merely permissive.
Pass { theme: "dark", fontSizes: 16 } and the typo gets caught, because fontSizes is not a property of Settings and therefore not a property of Partial<Settings> either. Without the type, that misspelling would silently do nothing and the setting would appear not to save.
Pick and Omit: sub-shapes without duplication
The server behind a web API should never send passwordHash to the browser. Instead of writing a second interface by hand and keeping it in sync forever, derive it:
interface User { id: number; name: string; email: string; passwordHash: string; } type PublicUser = Omit<User, "passwordHash">; type UserPreview = Pick<User, "id" | "name">;
PublicUser has everything except the hash. UserPreview has only id and name, note the union of literal property names from Unit 4. When User gains a field, the derived types update themselves.
updateTask: a partial update that does not mutate
updateTask takes a task and a partial set of changes, then returns a new task with the changes merged in.
interface Task { title: string; done: boolean; } function updateTask(task: Task, changes: Partial<Task>): Task { return { ...task, ...changes }; } const t: Task = { title: "study", done: false }; const finished = updateTask(t, { done: true }); console.log(finished.title + " done: " + finished.done); console.log(t.title + " done: " + t.done);
Output
study done: true study done: false
Reading the merge
- Spreading
taskfirst andchangessecond means the changes win wherever both define a property. Reversing the order would make the update do nothing, since the original values would overwrite it. - The
titlesurvives untouched becausechangesnever mentions it, which is exactly whatPartialis for. - The second printed line proves the original object was not mutated.
updateTaskbuilt a new one, which is the pattern React state updates and most modern data handling rely on.