Skip to main content

Readonly<Type> Utility Types in TypeScript With Examples

What is Readonly<Type>

Readonly<Type> in TypeScript is a utility type that makes all properties of a given type "Type" read-only. This means once you assign values to the properties of an object with this type, you cannot change them later. It is a way to ensure that an object remains constant and cannot be accidentally modified after it is created.

How Readonly<Type> Works

The syntax for Readonly<Type> in TypeScript is quite simple. Here's a breakdown of the syntax:

  • Readonly: This is the TypeScript utility type that is used to create a new type with all properties as read-only.
  • <Type>: This is a placeholder for the actual type you want to make read-only. You need to replace "Type" with the name of the type you're working with.

Let's say you have an interface called Person:

interface Person {
name: string;
age: number;
}

Now you want to create a read-only version of this interface. You can use the Readonly<Type> utility like this:

type ReadonlyPerson = Readonly<Person>;

In this example, Readonly<Person> creates a new type called ReadonlyPerson with the same properties as Person, but all properties are now read-only. This means you can't change the values of name and age once they are assigned for objects of type ReadonlyPerson.

TypeScript Course Instructor Image
TypeScript Course Instructor Image

Time To Transition From JavaScript To TypeScript

Level Up Your TypeScript And Object Oriented Programming Skills. The only complete TypeScript course on the marketplace you building TypeScript apps like a PRO.

SEE COURSE DETAILS

Let's create a person object and then create a readonlyPerson object to demonstrate that the values cannot be changed. Here's an example:

const person: Person = {
name: "John",
age: 30,
};

const readonlyPerson: ReadonlyPerson = {
name: "Jane",
age: 28,
};

At this point, you can modify the properties of the person object without any issues, because it is of type Person:

person.name = "John Doe";
person.age = 31;

However, if you try to modify the properties of the readonlyPerson object, TypeScript will throw an error because its properties are read-only:

readonlyPerson.name = "Jane Doe";
// Error: Cannot assign to 'name' because it is a read-only property.
readonlyPerson.age = 29;
// Error: Cannot assign to 'age' because it is a read-only property.

As you can see, TypeScript prevents you from changing the values of the readonlyPerson object because its type is ReadonlyPerson, which was created using the Readonly<Type> utility.

When to Use Readonly<Type>

Let's consider an example of a simple task management application. In this application, you have a list of tasks, and each task has a title, description, and status (completed or not completed).

Here's a possible use case for Readonly<Type> in this application:

  1. You have a function that fetches the list of tasks from an API.
  2. You want to make sure that once the tasks are fetched and stored in the application, they cannot be accidentally modified, ensuring data consistency.

First, let's define a Task interface:

interface Task {
id: number;
title: string;
description: string;
completed: boolean;
}

Now create a ReadonlyTask type using the Readonly<Type> utility:

type ReadonlyTask = Readonly<Task>;

Let's say you have a function called fetchTasks that fetches tasks from an API and returns an array of ReadonlyTask:

// Function to fetch tasks (dummy implementation)
async function fetchTasksFromAPI(): Promise<Task[]> {
// Create an array of dummy tasks
const tasks: Task[] = [
{
id: 1,
title: "Task 1",
description: "Description for Task 1",
completed: false,
},
{
id: 2,
title: "Task 2",
description: "Description for Task 2",
completed: true,
},
{
id: 3,
title: "Task 3",
description: "Description for Task 3",
completed: false,
},
];

// Mimic an asynchronous operation by returning a
// Promise that resolves with the dummy tasks
return new Promise((resolve) => {
setTimeout(() => {
resolve(tasks);
}, 1000); // Simulate a 1-second delay
});
}

async function fetchTasks(): Promise<ReadonlyTask[]> {
// Fetch tasks from an API and
// store them in a variable called tasks
const tasks: Task[] = await fetchTasksFromAPI();

// Convert tasks to readonly tasks
const readonlyTasks: ReadonlyTask[] = tasks.map((task) => {
return {
id: task.id,
title: task.title,
description: task.description,
completed: task.completed,
};
});

return readonlyTasks;
}

Now, when you fetch tasks using the fetchTasks function, you'll get an array of ReadonlyTask objects. This ensures that the tasks fetched from the API cannot be accidentally modified while working with them in your application. We can check this this as well by calling the function and tryiong to modify one of the tasks that is returned from the fetchTasks function:

const tasks = await fetchTasks();

tasks[0].title = "New Title";
// Error: Cannot assign to 'title' because it is a read-only property.

In this example, using Readonly<Type> helps maintain data consistency and prevents unintended modifications of the task objects throughout the application.

What Can You Do Next 🙏😊

If you liked the article, consider subscribing to Cloudaffle, my YouTube Channel, where I keep posting in-depth tutorials and all edutainment stuff for software developers.

YouTube @cloudaffle