Readonly Values in TypeScript

Readonly values in TypeScript prevent accidental mutation. Learn readonly properties, ReadonlyArray, readonly tuples, the Readonly utility type, and how readonly differs from const.

5 min read

The readonly modifier prevents a property or array element from being reassigned after it is set. It is a compile-time check only with zero runtime cost. Think of readonly as a property-level version of const: while const prevents reassignment of a variable, readonly prevents reassignment of a property inside an object.

Readonly Properties

Add readonly before a property name in an interface or type alias. The property can be read freely, but any attempt to reassign it produces a compiler error.

typescripttypescript
interface User {
  readonly id: number;
  name: string;
}
 
const user: User = { id: 1, name: "Alice" };
 
console.log(user.id);   // OK
user.name = "Bob";      // OK
user.id = 2;
// Error: Cannot assign to 'id' because it is a read-only property

The id property is locked. The name property is writable as usual. This is useful for fields that should be set once and never changed, like database IDs, creation timestamps, or foreign keys.

Readonly is shallow. It protects the property reference, not the value inside.

If the readonly property holds an object, the nested properties remain mutable. For deep immutability, you need as const or recursive readonly types.

Readonly Arrays

TypeScript provides ReadonlyArray for arrays that should not be mutated. The shorthand is readonly followed by the element type in square brackets. All mutating methods like push, pop, and splice are blocked.

typescripttypescript
function printScores(scores: readonly number[]) {
  console.log(scores[0]);    // OK
  console.log(scores.length); // OK
  scores.push(100);
  // Error: 'push' does not exist on 'readonly number[]'
}

Read-only methods like slice, map, filter, and reduce still work because they return new arrays without modifying the original. You can assign a regular array to a readonly array variable, but not the other way around. This one-way assignability is intentional: it means you can pass a mutable array to any function that expects a readonly array, but a function receiving a readonly array cannot accidentally mutate the caller's data.

Use readonly arrays in function parameters whenever the function reads but does not modify the array. It documents the contract and prevents accidental internal mutations.

Readonly Tuples

Tuples can also be readonly. Prefix the tuple type with the readonly keyword. Arrays created with as const are automatically readonly tuples, which means passing them to functions expecting mutable tuples will fail.

typescripttypescript
const point = [3, 4] as const;
// Type: readonly [3, 4]
 
function distance([x, y]: readonly [number, number]) {
  return Math.sqrt(x ** 2 + y ** 2);
}
 
distance(point); // OK

If the parameter were declared as a mutable tuple, the call would fail because readonly tuples are not assignable to mutable ones. Always declare tuple parameters as readonly when the function does not modify them.

The Readonly Utility Type

TypeScript provides a built-in Readonly utility type that makes every property of a given type readonly in one line. You do not need to create a separate immutable interface.

typescripttypescript
interface Todo {
  title: string;
  completed: boolean;
}
 
function markComplete(todo: Readonly<Todo>) {
  console.log(todo.title); // OK
  todo.completed = true;
  // Error: Cannot assign to 'completed' because it is a read-only property
}

Use the Readonly utility type when you want to pass an object to a function and guarantee it will not be modified. It works with any object type, including interfaces, type aliases, and inline object types.

Readonly vs Const vs As Const

These three features serve different purposes and are often used together:

FeatureWhat it protectsDepth
constVariable bindingShallow
readonlyProperty or array elementShallow
as constEntire object or array with literal typesDeep

You can combine all three. A const variable with as const gives you a deeply immutable object where every property is readonly and every value has a literal type. This single assertion provides maximum type safety with no extra syntax beyond the two keywords.

Common Mistakes

The biggest misunderstanding is expecting readonly to be deep. A readonly object property still contains mutable inner properties. The modifier only protects the property itself, not the object tree below it.

Another common mistake is forgetting that readonly is compile-time only. At runtime, readonly properties behave like any other property. An object can be mutated through aliasing even when the type says readonly.

The modifier prevents accidental mutations in your code, but it cannot stop deliberate mutations through shared references.

For more on how as const works with literal types and immutability, see TypeScript literal types explained. For the difference between const bindings and readonly properties, see Let and const in TypeScript.

Rune AI

Rune AI

Key Insights

  • readonly prevents reassignment of a property, not deep immutability.
  • ReadonlyArray blocks mutating methods like push and splice at compile time.
  • The Readonly utility type makes all properties of a type readonly in one line.
  • readonly is compile-time only; it does not exist in the JavaScript output.
  • Use readonly in function parameters to prevent accidental mutations.
RunePowered by Rune AI

Frequently Asked Questions

What is the difference between const and readonly?

const prevents reassignment of a variable. readonly prevents reassignment of a property. const is for variables, readonly is for properties inside objects, interfaces, and types.

Does readonly affect runtime behavior?

No. readonly is purely a compile-time check. The JavaScript output has no readonly enforcement. It prevents accidental writes during development but cannot stop runtime mutations through aliasing.

Can I make a readonly property mutable again?

Not directly. But you can use a mapped type with the -readonly modifier to strip readonly from all properties. Or you can assign a readonly object to a mutable type variable if the structure matches.

Conclusion

readonly is a lightweight way to signal that a property or array should not be changed. It catches accidental mutations at compile time with zero runtime cost. Use readonly properties on interfaces that represent data you consume but should not modify. Use ReadonlyArray for function parameters to promise callers you will not mutate their arrays. Pair readonly with const and as const for a complete immutability strategy.