Learn TypeScript

Creating readonly objects and array properties

Creating readonly objects and array properties

In this lesson, we will learn how to create object types with readonly properties that are objects and arrays.

Understanding the readonly modifier on reference types

The readonly modifier can be placed before a property that is a reference type (i.e. an object or an array) as follows:

type TypeName = {
readonly propertyName1: ObjectType;
readonly propertyName2: ArrayType;
};

This can also be used in interfaces and classes:

interface InterfaceName {
readonly propertyName1: ObjectType;
readonly propertyName2: ArrayType;
}
class ClassName {
constructor(
public readonly propertyName1: ObjectType,
public readonly propertyName2: ArrayType
) {}
}

This will stop the property's reference from changing so that it can't be changed to another object or array. It, however, doesn't stop values within the property from changing.

We can put an additional readonly modifier before array type to prevent the array items from changing for array types.

readonly propertyName: readonly ArrayType;

For object types, we need to put an additional readonly modifier before properties within the object to prevent them from changing.

type TypeName = {
readonly propertyName: {
readonly subPropertyName: PropertyType;
};
};

An example

We will explore the readonly modifier on an object and array properties in the TypeScript playground.

  • Open the TypeScript playground by clicking the link below:
Open TypeScript Playground
  • Paste the code from below into the TypeScript Playground:
interface Result {
name: string;
readonly scores: number[];
readonly profile: {
level: number;
};
}
let billScores: Result = {
name: "Bill",
scores: [90, 65, 80],
profile: {
level: 1,
},
};
console.log(billScores);

The code contains a variable given a Result type, which in tern has readonly array and object properties.

  • Let's try to add a new value to the scores array property. Put the following line of code just before the console.log statement in the editor.
billScores.scores.push(70);
🤔

Why isn't a type error raised on this line of code? How can we ensure a type error is raised?

  • Now add a line of code to change the level property. Again, put the following line of code just before the console.log statement in the editor.
billScores.profile.level = 2;
🤔

Why isn't a type error raised on this line of code? How can we ensure a type error is raised?

  • Click the Run option in the TypeScript Playground and open the console.
🤔

Did the changes to the scores array and level take place at runtime?

Using the Readonly utility type

There is a handy utility type called Readonly that automatically adds the readonly modifier to all the properties in an object type:

type ReadonlyType = Readonly<ExistingType>;
  • Remove all the readonly modifiers from the Result type:
interface Result {
name: string;
scores: number[];
profile: {
level: number;
};
}
  • Let's use the Readonly utility type on the billScores variable:
let billScores: Readonly<Result> = {
name: "Bill",
scores: [90, 65, 80],
profile: {
level: 1,
},
};
  • Add a line of code to change the name property in billScores:
billScores.name = "Bob";

A type error occurs as we expect.

🤔

Why doesn't a type error occur on the other two assignments?

billScores.scores.push(70);
billScores.profile.level = 2;

Summary

Care needs to be taken when adding the readonly modifier to object and array properties. If the requirement is to make an array property value immutable, an additional readonly modifier needs to be added before the array type. If the requirement is to make an object property value deep immutable, additional readonly modifiers need to be added to all the nested properties.

Care also 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.

In the next lesson, we will learn how to make objects and arrays immutable at runtime and compile time.

© 2020 Carl Rippon
Privacy Policy
This site uses cookies. Click here to find out more