FrontendPrep
Menu
Topics
Questions
Guides
Challenges
Soon
Back to TypeScript Questions
typescriptEasy

TypeScript: Readonly and Utility Modifiers

Understand how to enforce immutability in TypeScript. Learn about the readonly modifier, Readonly<T> utility type, const assertions, and readonly arrays.

TypeScript: Readonly and Utility Modifiers

One of the common frontend interview questions that focuses on code styling and clean architecture is:

How do you declare immutable properties in TypeScript? What is the difference between readonly, the Readonly<T> utility type, and as const assertions?

TypeScript provides multiple mechanisms to enforce compile-time immutability, allowing you to catch unintended side effects (like mutating state objects directly instead of using setState/reducers).


1. The readonly Property Modifier

The readonly keyword is applied to individual properties in interfaces, type aliases, or classes:

interface User {
  readonly id: string;
  name: string;
}
 
const user: User = { id: "123", name: "Alice" };
user.name = "Bob"; // ✅ Allowed
user.id = "456";   // ❌ Compile Error: Cannot assign to 'id' because it is a read-only property.

Class usage:

class Config {
  readonly apiUrl: string;
  constructor(url: string) {
    this.apiUrl = url; // ✅ Allowed only in constructors
  }
}

2. The Readonly<T> Utility Type

Instead of manually adding readonly to every key in a large object shape, you can wrap the entire type in the Readonly<T> utility type, which maps all properties to read-only recursively.

interface State {
  theme: string;
  isLoading: boolean;
}
 
const state: Readonly<State> = {
  theme: "dark",
  isLoading: false
};
 
state.theme = "light"; // ❌ Compile Error: Cannot assign to 'theme' because it is a read-only property.

3. Readonly Arrays (ReadonlyArray<T> / readonly Type[])

Standard arrays in JavaScript are mutable (methods like push(), splice(), and sort() modify the original array in place). TypeScript provides a specific immutable array type:

const numbers: readonly number[] = [1, 2, 3];
// Alternative: const numbers: ReadonlyArray<number> = [1, 2, 3];
 
numbers.push(4);  // ❌ Compile Error: Property 'push' does not exist on type 'readonly number[]'.
numbers[0] = 10;  // ❌ Compile Error: Index signature in type 'readonly number[]' only permits reading.

4. readonly vs. const vs. as const

It is common to confuse these constructs. Here is how they stack up:

  • const: A standard JavaScript runtime variable declaration. It prevents variable re-assignment, but does not prevent mutating the object's properties.
  • readonly: A TypeScript compiler flag. It prevents property reassignment on an object at compile-time.
  • as const: A const assertion. It locks down both the array/object references, keeps literal types from widening, and recursively sets all nested properties to readonly.
const config = {
  port: 8080
} as const;
 
config.port = 9000; // ❌ Compile Error: Cannot assign because it is read-only

Key Contrast Summary

Featureconst variablereadonly propertyas const assertion
System LevelJavaScript RuntimeTypeScript CompilerTypeScript Compiler
ScopeVariable ReferenceObject PropertyEntire Literal Expression
Deep Immutability❌ No❌ No (requires nesting)✅ Yes (recursively sets all to read-only)
Type Widening✅ Widen strings to string✅ Widened❌ Kept as strict literal types

Key Takeaways

  • readonly Modifier: Prevents properties on an interface or class from being reassigned after initialization.
  • Readonly Utility: Wraps an entire object type to make all its properties read-only without manual keys.
  • as const Assertions: Locks down literal values recursively to read-only, preventing type widening to primitive types.
  • ReadonlyArray: Prevents mutating array methods like push and pop from running on an array at compile-time.

Share this Resource

Help other developers level up by sharing this study guide.

More Technical Questions

Expand your mastery. Deep dive into other frontend interview challenges in this category.