M Logo
๐Ÿ“ TypeScript from Zero ยท Part 32 of 50

TypeScript Utility Types: Partial, Pick, Omit, Required, Readonly, ReturnType

November 6, 2026

Welcome to episode 32 of TypeScript from Zero, a series for complete beginners (no JavaScript needed). Welcome back to TypeScript from zero! Sometimes we need a type that is almost like another one: the same, but with some parts changed. Writing it again would be a waste. Utility types build new types from old ones. We continue in the tasks project, with a new file called update.ts. Every code block below is the exact code and output from the video, ready to copy.

Setup

We start in the terminal and open the project in VS Code:

bash
cd tasks
touch src/update.ts
code .

1. Type

We start with a flat type for the data of a task: an id, a title, a priority number, and an optional note. And one example value.

2. Partial Omit

An update function takes a task and a set of changes. The changes may be any part of the task, but never the id. Omit removes keys from a type. Partial makes every property optional. Together, they describe exactly what we want. The function spreads the item, then the changes on top. Changing only the priority works, and the rest stays as it was.

src/update.ts:

ts
type TaskData = {
  id: number;
  title: string;
  priority: number;
  note?: string;
};
const base: TaskData = { id: 1, title: "Buy milk", priority: 2 };

type Changes = Partial<Omit<TaskData, "id">>;
function update(item: TaskData, changes: Changes): TaskData {
  return { ...item, ...changes };
}
console.log(update(base, { priority: 5 }));
text
$ node src/update.ts
{ id: 1, title: 'Buy milk', priority: 5 }

3. Error

And if somebody tries to change the id? Node would allow it. tsc says: object literal may only specify known properties, and id does not exist in the type Partial Omit task data. TS2353.

src/update.ts:

ts
type TaskData = {
  id: number;
  title: string;
  priority: number;
  note?: string;
};
const base: TaskData = { id: 1, title: "Buy milk", priority: 2 };

type Changes = Partial<Omit<TaskData, "id">>;
function update(item: TaskData, changes: Changes): TaskData {
  return { ...item, ...changes };
}
console.log(update(base, { priority: 5 }));
console.log(update(base, { id: 99 }));
text
$ node src/update.ts
{ id: 1, title: 'Buy milk', priority: 5 }
{ id: 99, title: 'Buy milk', priority: 2 }
text
$ npx tsc --noEmit
src/update.ts:14:28 - error TS2353: Object literal may only specify known properties, and 'id' does not exist in type 'Partial<Omit<TaskData, "id">>'.

14 console.log(update(base, { id: 99 }));
                              ~~

Found 1 error in src/update.ts:14

4. Fix

We delete that line, and tsc is quiet again.

src/update.ts:

ts
type TaskData = {
  id: number;
  title: string;
  priority: number;
  note?: string;
};
const base: TaskData = { id: 1, title: "Buy milk", priority: 2 };

type Changes = Partial<Omit<TaskData, "id">>;
function update(item: TaskData, changes: Changes): TaskData {
  return { ...item, ...changes };
}
console.log(update(base, { priority: 5 }));
bash
$ npx tsc --noEmit

5. Pick

Pick is the opposite of Omit: we choose the keys that we want to keep. A summary needs only the id and the title.

src/update.ts:

ts
type TaskData = {
  id: number;
  title: string;
  priority: number;
  note?: string;
};
const base: TaskData = { id: 1, title: "Buy milk", priority: 2 };

type Changes = Partial<Omit<TaskData, "id">>;
function update(item: TaskData, changes: Changes): TaskData {
  return { ...item, ...changes };
}
console.log(update(base, { priority: 5 }));
type Summary = Pick<TaskData, "id" | "title">;
const s: Summary = { id: 1, title: "Buy milk" };
console.log(s);
text
$ node src/update.ts
{ id: 1, title: 'Buy milk', priority: 5 }
{ id: 1, title: 'Buy milk' }

6. Required Readonly

Required makes every optional property required, so the note must exist. Readonly makes every property read only. If we try to change the title of a frozen task, tsc stops us: cannot assign to title because it is a read only property. TS2540.

src/update.ts:

ts
type TaskData = {
  id: number;
  title: string;
  priority: number;
  note?: string;
};
const base: TaskData = { id: 1, title: "Buy milk", priority: 2 };

type Changes = Partial<Omit<TaskData, "id">>;
function update(item: TaskData, changes: Changes): TaskData {
  return { ...item, ...changes };
}
console.log(update(base, { priority: 5 }));
type Summary = Pick<TaskData, "id" | "title">;
const s: Summary = { id: 1, title: "Buy milk" };
console.log(s);
type Full = Required<TaskData>;
const frozen: Readonly<TaskData> = base;
frozen.title = "New";
text
$ npx tsc --noEmit
src/update.ts:19:8 - error TS2540: Cannot assign to 'title' because it is a read-only property.

19 frozen.title = "New";
          ~~~~~

Found 1 error in src/update.ts:19

7. Fix

We delete the assignment.

src/update.ts:

ts
type TaskData = {
  id: number;
  title: string;
  priority: number;
  note?: string;
};
const base: TaskData = { id: 1, title: "Buy milk", priority: 2 };

type Changes = Partial<Omit<TaskData, "id">>;
function update(item: TaskData, changes: Changes): TaskData {
  return { ...item, ...changes };
}
console.log(update(base, { priority: 5 }));
type Summary = Pick<TaskData, "id" | "title">;
const s: Summary = { id: 1, title: "Buy milk" };
console.log(s);
type Full = Required<TaskData>;
const frozen: Readonly<TaskData> = base;
bash
$ npx tsc --noEmit

8. ReturnType Awaited

Two more tools get a type from a function. Return type gives the type that a function returns. Our load tasks is async, so that is a promise. Awaited takes the promise away. Together we get a list of tasks, without writing the type a second time. If load tasks ever changes, this type changes with it.

src/update.ts:

ts
import { loadTasks } from "./store.ts";
type TaskData = {
  id: number;
  title: string;
  priority: number;
  note?: string;
};
const base: TaskData = { id: 1, title: "Buy milk", priority: 2 };

type Changes = Partial<Omit<TaskData, "id">>;
function update(item: TaskData, changes: Changes): TaskData {
  return { ...item, ...changes };
}
console.log(update(base, { priority: 5 }));
type Summary = Pick<TaskData, "id" | "title">;
const s: Summary = { id: 1, title: "Buy milk" };
console.log(s);
type Full = Required<TaskData>;
const frozen: Readonly<TaskData> = base;
type Loaded = Awaited<ReturnType<typeof loadTasks>>;
const list: Loaded = [];
console.log(list.length);
text
$ node src/update.ts
{ id: 1, title: 'Buy milk', priority: 5 }
{ id: 1, title: 'Buy milk' }
0

Recap

Today: Partial, Required, Readonly, Pick and Omit, and return type with awaited. Next episode: we build the project, and install our tool on the computer.

ยฉ 2026 All Rights Reserved By Mahmudul Alam