TypeScript Utility Types in Use
Utility types reshape an existing type without rewriting it. Partial, Required and Readonly each change how the members of a value may be used.
The patch pattern with Partial
The update function takes an id and a partial patch. It applies the patch over the stored record and returns the merged result.
interface StoredUser {
id: number;
displayName: string;
email: string;
}
const stored: StoredUser = {
id: 1,
displayName: "Asha",
email: "asha@example.com",
};
function updateStored(id: number, patch: Partial<StoredUser>): StoredUser {
return { ...stored, ...patch, id };
}
const updated = updateStored(1, { email: "asha.rao@example.com" });
console.log(updated.id);
console.log(updated.displayName);
console.log(updated.email);Required as the gate before saving
A draft is built loosely with optional members. The save function accepts only a Required value, so every member must be present first.
interface DraftArticle {
title?: string;
body?: string;
}
function saveArticle(article: Required<DraftArticle>): string {
return "Saved " + article.title;
}
const draft: DraftArticle = { title: "Guide" };
console.log(draft.body);
const checked: Required<DraftArticle> = {
title: "Guide",
body: "Full text",
};
console.log(saveArticle(checked));
console.log(checked.body);Readonly guards one level only
The address object sits inside a Readonly value. Changing its city still runs, and the output proves the change went through.
interface AddressBlock {
city: string;
}
interface CustomerRecord {
customerName: string;
address: AddressBlock;
}
const customer: Readonly<CustomerRecord> = {
customerName: "Asha",
address: { city: "Pune" },
};
customer.address.city = "Mumbai";
console.log(customer.customerName);
console.log(customer.address.city);Example
interface ProductRecord {
id: number;
title: string;
price: number;
}
const product: ProductRecord = { id: 9, title: "Pen", price: 250 };
function patchProduct(
record: ProductRecord,
patch: Partial<ProductRecord>
): ProductRecord {
return { ...record, ...patch };
}
const patched = patchProduct(product, { price: 199 });
const view: Readonly<ProductRecord> = patched;
console.log(view.id);
console.log(view.title);
console.log(view.price);Key points
- Partial<T> makes every member optional, which suits patch-style updates.
- Required<T> removes optionality, which suits a gate before saving.
- Readonly<T> guards only the top level of a value.
- A nested object inside a Readonly value stays mutable.
In this example, a Partial patch lowers the price, and the merged record is then exposed through a Readonly view.
Note
Readonly is a checker guard, not a runtime freeze of the object.
Try it Yourself
Edit the code, then press Run.
Running is simulated ยท TypeScript Playground
Exercise
A Readonly value holds a nested address object. What happens when code changes the city inside that address?