TypeScript and the ReadOnly option

TypeScript and the ReadOnly option

Β·

3 min read

When it comes to TypeScript, there is yet another modifier we haven't touched. This is readonly, which can be used to make fields read-only.

Meaning we are not allowed to change them after they initialize.

To demonstrate how it works, we'll look at how we can define interface properties as read-only and how we can alter those later on.

TypeScript readonly interface

The cool part about the readonly modifier is that we can even use it on the interface declaration, making specific fields read-only from the start.

It works by prefixing the type with readonly like this:

interface User {
  readonly id?: number;
  firstname: string;
  lastname: string;
  age?: number;
}

The only time we can ever set this value is on initialising it like this:

const user:User = {
  id: 123,
  firstname: 'Felix',
  lastname: 'Bongers'
}

As you know, we can change the firstname field, for instance:

user.firstname = 'Chris'

But when we try and modify the ID field, we get an error.

user.id = 12

TypeScript readonly error

This can be super useful for fields that you want to ensure can never change.

TypeScript ReadOnly utility type

We can also leverage a utility type to change a property to read-only.

We had a specific look at this in the article on TypeScript Readonly Utility Type.

However, now that we also learned how to leverage Pick and Omit, we can narrow down the use case.

Let's say we have this User interface again but only want to make the ID read-only at a later stage.

interface User {
  id?: number;
  firstname: string;
  lastname: string;
  age?: number;
}

Now we could simply use the hack we used for Generics and Utility Types like this:

type IdReadOnly = Readonly<Pick<User, 'id'>> & Omit<User, 'id'>;

Or even make this a generic re-usable type.

type ReadOnlyByKey<T, K extends keyof T> = Readonly<Pick<T, K>> & Omit<T, K>;

Which in return we can use like this:

type IdReadOnly = ReadOnlyByKey<User, 'id'>;

All these versions will make the id field read-only from that type on.

Removing the read-only modifier

There might be cases where you want to undo the read-only modifier. And this particular removal is unique to the read-only property.

This is called mapping modifiers, and there are only two of them: readonly and ? (optional).

For instance to remove all occurrences of a readonly attribute we can do the following:

type Mutable<T> = {
   -readonly [k in keyof T]: T[k];
};

This removes all readonly attributes since we used the - sign. If you removed the -, all fields would be read-only.

Let's try this out for a second and take the first example we had.

interface User {
  readonly id?: number;
  firstname: string;
  lastname: string;
  age?: number;
}

Before, we could not modify the id field, so let's convert this into a mutable type.

type Mutable<T> = {
  -readonly [k in keyof T]: T[k];
};

const user:Mutable<User> = {
  id: 123,
  firstname: 'Felix',
  lastname: 'Bongers'
}

And now we can modify the id field again!

I hope you learned a lot about changing the read-only property of a type/interface. Do let me know if you have any questions in this regard.

Thank you for reading, and let's connect!

Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter

Did you find this article valuable?

Support Daily Dev Tips by becoming a sponsor. Any amount is appreciated!