Built-in Utility Types
TypeScript ships with a set of global utility types that transform existing types. They are generic wrappers that eliminate the need to write repetitive type transformations manually. Every TypeScript developer uses them daily.
Utility Types — Complete Reference
// Setup — base types for examples
interface Task {
id: number;
title: string;
description: string | null;
status: "todo" | "in-progress" | "done" | "archived";
priority: "low" | "medium" | "high" | "critical";
assigneeId: number | null;
dueDate: Date | null;
createdAt: Date;
updatedAt: Date;
}
// ── Partial<T> — all properties optional ──────────────────────
type TaskPatch = Partial<Task>;
// { id?: number; title?: string; status?: ...; ... }
function updateTask(id: number, patch: Partial<Task>): Task {
const task = taskRepo.findById(id)!;
return { ...task, ...patch, updatedAt: new Date() };
}
updateTask(1, { status: "done" }); // ✅ only update status
updateTask(1, { title: "New name", priority: "high" }); // ✅
// ── Required<T> — all properties required (undo optional) ──────
type RequiredTask = Required<Task>;
// Removes all ? — every field is mandatory
// ── Readonly<T> — all properties readonly ──────────────────────
type ImmutableTask = Readonly<Task>;
// Creates a frozen view of Task — useful for function params
function displayTask(task: Readonly<Task>): void {
// task.status = "done"; // ❌ Error: readonly
console.log(task.title); // ✅
}
// ── Pick<T, K> — select subset of properties ─────────────────
type TaskSummary = Pick<Task, "id" | "title" | "status" | "priority">;
// { id: number; title: string; status: ...; priority: ... }
// Used for: API list responses (don't send full data)
// ── Omit<T, K> — exclude specific properties ──────────────────
type CreateTaskInput = Omit<Task, "id" | "createdAt" | "updatedAt">;
// User provides all fields EXCEPT auto-generated ones
// Used for: API request bodies, form data types
type PublicTask = Omit<Task, "assigneeId">; // hide internal fields
// ── Record<K, V> — typed dictionary ──────────────────────────
type StatusLabel = Record<Task["status"], string>;
const labels: StatusLabel = {
"todo": "📌 To Do",
"in-progress": "⚡ In Progress",
"done": "✅ Done",
"archived": "🗄️ Archived",
};
// ── Exclude<T, U> — remove types from union ───────────────────
type ActiveStatus = Exclude<Task["status"], "archived">;
// "todo" | "in-progress" | "done" (archived removed)
// ── Extract<T, U> — keep only matching from union ─────────────
type FinishedStatus = Extract<Task["status"], "done" | "archived">;
// "done" | "archived"
// ── NonNullable<T> — removes null and undefined ───────────────
type SafeDate = NonNullable<Task["dueDate"]>; // Date (not Date | null)
// ── ReturnType<F> — extract return type of function ──────────
async function fetchTask(id: number): Promise<Task> { /* ... */ return {} as Task; }
type FetchResult = ReturnType<typeof fetchTask>; // Promise<Task>
type Unwrapped = Awaited<FetchResult>; // Task
// ── Parameters<F> — extract function parameter types ─────────
type FetchParams = Parameters<typeof fetchTask>; // [id: number]
// ── InstanceType<C> — type of class instance ─────────────────
class TaskService { /* ... */ }
type TaskServiceInstance = InstanceType<typeof TaskService>; // TaskServiceCommon Mistakes
- Using `Partial<T>` for update operations without validation — `Partial<Task>` allows sending `{}` (empty patch). Always validate that at least one field is present at runtime.
- Confusing `Omit` and `Pick` — `Omit<T, K>` removes K from T (keep everything except); `Pick<T, K>` keeps only K from T. For create inputs, `Omit<T, 'id'>` is idiomatic.
- Not using `ReturnType` when reusing function return types — copy-pasting return types creates drift. Use `ReturnType<typeof myFn>` so the return type auto-updates when the function changes.
Tip
Tip
Practice Builtin Utility Types in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
interface for objects. type for unions and computed.
Practice Task
Note
Practice Task — (1) Write a working example of Builtin Utility Types from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with Builtin Utility Types is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready typescript code.
Key Takeaways
- TypeScript ships with a set of global utility types that transform existing types.
- Using `Partial<T>` for update operations without validation — `Partial<Task>` allows sending `{}` (empty patch). Always validate that at least one field is present at runtime.
- Confusing `Omit` and `Pick` — `Omit<T, K>` removes K from T (keep everything except); `Pick<T, K>` keeps only K from T. For create inputs, `Omit<T, 'id'>` is idiomatic.
- Not using `ReturnType` when reusing function return types — copy-pasting return types creates drift. Use `ReturnType<typeof myFn>` so the return type auto-updates when the function changes.