TypeScript Readonly Utility Type

📝 Table Of Contents

☕️ Summary

The Readonly<Type> utility type constructs a type with all properties of Type set to readonly where the properties of the newly constructed type cannot be reassigned.

Continuing on with our guide through utility types, we're going to create another function called freezeUser that will freeze an object with the JavaScript Object.freeze() method and return a user object with readonly properties.

This will also demonstrate the difference between compile-time immutability and run-time immutability.

▶️ Code Sandbox Example

🛠 How It Works

So, let's take a look at the freezeUser function we've created.

// User.tsx

export function freezeUser(user: User): Readonly<User> {
  return user;

We take in a parameter user of type User and the return type of the function is Readonly<User> which is a newly constructed User with all properties set to readonly. This means that it is readonly to the Typescript compiler, so the compiler will warn us if we attempt to reassign any of the properties.

Also, in the freezeUser function, we use Object.freeze() and return the frozen object which also will not update the age of user4 at runtime.

// App.tsx

let user4 = freezeUser(user3);

user4.age = 55;
// Cannot assign to 'lastName' because it is a read-only property.

But then what if we reassign user4 to a new object that is not frozen, yet still readonly?

Let's re-use the updateUser function from the first guide for using the Partial utility type ↗ which will reassign user4 to an object that is no longer frozen.

// App.tsx

// Now we're reassigning user4 to a new object that is returned
// from the updateUser() function which means it's no longer frozen.
user4 = updateUser(user4, { firstName: "Alien" });

user4.lastName = "Musk";
// Cannot assign to 'age' because it is a read-only property.

We still get the compiler warning us that user4.lastname is read-only but this still does not stop that value of those properties from changing at run-time though. The UI will still update the value at runtime.

🧠 Quiz Question

Given an interface Block with 2 properties hash and message:

interface Block = {
  hash: string;
  message?: string;

If we wanted to define a new type StrictBlock with Readonly<Block> like this:

type StrictBlock = Readonly<Block>;

Which of the following is true of StrictBlock?

Test your knowledge 🧠