Utility Type Readonly Explained

I'd like to start by saying that I am doing this series to learn and understand better Typescript, so feel free to correct me or contact me.

What is the Readonly Utility Type?

Readonly<Type>

Constructs a type with all properties of Type set to readonly, meaning the properties of the constructed type cannot be reassigned. Docs.

Let's see an example of it:

interface Person {
  name: string;
}

const person: Readonly<Person> = {
  name: "Joe",
  schedule: {
    starts: "08:00",
    ends: "16:00"
  }
};

person.name = "Tom"; 
// Cannot assign to 'title' because it is a read-only property.

As you can see, the name property can't be reassigned.

The utility type is basically doing the following:

interface Person {
  readonly name: string;
  readonly schedule: ...
}

One thing to keep in mind is that it doesn't add the readonly for nested properties. For example, it won't add the readonly to the starts type too. Additional readonly modifiers need to be added to all the nested properties.

person.schedule.starts = "12:00"; // This is ok 

But how does the Readonly Utility works under the hood ?

type MyReadonly<Type> = {
  readonly [Key in keyof Type] : Type[Key]
}

Okay, that is not too bad, let's break it down.

First we add the readonly keyword to every property type.

Then [Key in keyof Type] the keyword in means that Key has to be one of the keys extracted Type("name" | "schedule").

Finally, Type[Key] here we are simply accessing the type of a property. Same as how we access values in an object, let's look at the following example.

Little side note: Remember that care needs to be taken when using the Readonly utility type. It is perfect for shallow object immutability but doesn't make an array or nested object properties immutable.

Thank you!