JavaScript's Object.freeze and TypeScript's Readonly

JavaScript's Object.freeze and TypeScript's Readonly

Published

Do you want static or runtime immutability, or both? In this article I will show you how to combine Object.freeze with Readonly to get the best of both worlds.

JavaScript’s Object.freeze

JavaScript’s Object.freeze() function provides runtime immutability (at a minor performance cost). You have a 100% guarantee your array or object will not change underneath you.

const austrians = Object.freeze([
  Object.freeze({name: "Carl Menger"}),
  Object.freeze({name: "Eugen Böhm von Bawerk"}),
  Object.freeze({name: "Ludwig von Mises"})
]);

austrians.push({name: "John Maynard Keynes"}); // Runtime Error
austrians[0].name = "John Maynard Keynes"; // Runtime Error

TypeScript’s Readonly

TypeScript’s Readonly<T> generic type provides static type immutability which has zero performance overhead. At runtime the array or object can change, so this only works if all your code is TypeScript.

interface Austrian {
  readonly name: string;
}

const austrians: ReadonlyArray<Austrian> = [
  {name: "Carl Menger"},
  {name: "Eugen Böhm von Bawerk"},
  {name: "Ludwig von Mises"}
];

austrians.push({name: "John Maynard Keynes"}); // Compile error
austrians[0].name = "John Maynard Keynes"; // Compile error

TypeScript ❤️ JavaScript

These two tools are not mutually exclusive you can combine Static typing immutability & runtime immutability.

interface Austrian {
  readonly name: string;
}

const austrians: ReadonlyArray<Austrian> = Object.freeze([
  Object.freeze({name: "Carl Menger"}),
  Object.freeze({name: "Eugen Böhm von Bawerk"}),
  Object.freeze({name: "Ludwig von Mises"})
]);

austrians.push({name: "John Maynard Keynes"}); // Everything error
austrians[0].name = "John Maynard Keynes"; // Everything error