How to fix Cannot assign to 'X' because it is a read-only property in TypeScript

TypeScriptBEGINNERMEDIUMUpdated Dec 8, 2025· 180 views

This TypeScript error occurs when you try to modify a property marked with the readonly modifier. The fix involves either removing the readonly modifier, creating a new object instead of mutating, or using a type utility to strip readonly from properties.

Quick fix

Fixes TS2540

interface User {
  readonly id: number;
  readonly name: string;
  email: string;
}

const user: User = { id: 1, name: "John", email: "john@example.com" };
user.id = 2; // Error: Cannot assign to 'id' because it is a read-only property
  1. Identify the readonly property
  2. Remove the readonly modifier (if appropriate)
  3. Create a new object instead of mutating (immutable approach)
  4. Use a Mutable utility type to strip readonly
typescript — error
interface User {
readonly id: number;
readonly name: string;
email: string;
}
const user: User = { id: 1, name: "John", email: "john@example.com" };
user.id = 2; // Error: Cannot assign to 'id' because it is a read-only property
Cannot assign to 'X' because it is a read-only property

What this error means

In TypeScript, the `readonly` keyword restricts properties from being assigned after their initial declaration. This is a compile-time safety feature that helps prevent unintended mutations. When you mark a property as `readonly`, TypeScript enforces that it can only be assigned: 1. At the point of declaration (inline) 2. In the class constructor 3. Never afterwards in instance methods or external code If you attempt to assign a new value to a readonly property anywhere else, TypeScript will emit error TS2540. This is a type-checking error only—it doesn't affect runtime behavior since JavaScript doesn't have a true readonly concept. Once compiled to JavaScript, the code will actually allow the assignment (which can lead to subtle bugs if you rely on readonly for immutability).

Typical symptoms

  • TypeScript compiler shows 'Cannot assign to X because it is a read-only property'
  • You're trying to modify an object property after it was created
  • Error occurs in class instance methods or when mutating objects outside the class
  • IDE shows red squiggly line under the assignment
  • The error mentions a specific property name that you're trying to assign
  • Code compiles and runs fine in JavaScript but fails TypeScript type checking

Common causes

  1. Property is declared with the readonly modifier (most common)
  2. Property comes from a readonly interface or type
  3. Class property is declared as readonly and you're trying to modify it outside the constructor
  4. You're trying to mutate a readonly object literal created with as const
  5. Inherited readonly properties from a parent class are being modified
  6. Using a third-party type definition with readonly constraints

How to fix "Cannot assign to 'X' because it is a read-only property"

1Identify the readonly property

First, locate which property is marked as readonly in your code. Look for the readonly keyword:

typescript
interface User {
  readonly id: number;
  readonly name: string;
  email: string;
}

const user: User = { id: 1, name: "John", email: "john@example.com" };
user.id = 2; // Error: Cannot assign to 'id' because it is a read-only property

In a class:

typescript
class Config {
  readonly apiUrl: string;

  constructor(url: string) {
    this.apiUrl = url; // OK: assignment in constructor
  }
}

const config = new Config("https://api.example.com");
config.apiUrl = "https://new-api.com"; // Error: Cannot assign
2Remove the readonly modifier (if appropriate)

If you genuinely need to mutate the property, simply remove the readonly keyword:

typescript
// Before
interface User {
  readonly id: number;
  readonly name: string;
}

// After
interface User {
  id: number;      // readable and writable
  name: string;
}

const user: User = { id: 1, name: "John" };
user.id = 2;       // Now OK

Note: Only do this if the property doesn't need immutability protection.

3Create a new object instead of mutating (immutable approach)

Instead of modifying the readonly property, create a new object with updated values:

typescript
interface User {
  readonly id: number;
  readonly name: string;
  readonly email: string;
}

const user: User = { id: 1, name: "John", email: "john@example.com" };

// Don't mutate; create a new object
const updatedUser: User = {
  ...user,
  email: "john.new@example.com",
};

// user.email is still "john@example.com" (unchanged)
// updatedUser.email is "john.new@example.com" (new object)

This approach is immutable and aligns with modern JavaScript practices. It's also TypeScript's recommended pattern for data transformations.

4Use a Mutable utility type to strip readonly

If you need to mutate a readonly type, create a utility type that removes the readonly modifier:

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

interface User {
  readonly id: number;
  readonly name: string;
}

// Create a mutable version
const user: Mutable<User> = { id: 1, name: "John" };
user.id = 2;         // Now OK
user.name = "Jane";  // Now OK

This is useful when working with APIs that return readonly types but you need to modify them locally. The -readonly mapped type modifier strips the readonly constraint.

5Use type assertion as a last resort

If you absolutely must bypass TypeScript's readonly check, use a type assertion (but be careful):

typescript
interface User {
  readonly id: number;
  readonly name: string;
}

const user: User = { id: 1, name: "John" };

// Type assertion bypasses the readonly check
(user as { id: number; name: string }).id = 2;

// Or more directly:
(user as any).id = 2;

Caution: This bypasses TypeScript's safety checks. Only use when you're certain of what you're doing and there's no better alternative. This approach negates the benefits of the readonly modifier.

6Modify assignments in constructors

If you're working with a class and need to set a readonly property, ensure you do it in the constructor:

typescript
class Config {
  readonly apiUrl: string;
  readonly timeout: number;

  constructor(url: string, timeout: number) {
    // These assignments in the constructor are allowed
    this.apiUrl = url;
    this.timeout = timeout;
  }

  updateUrl(newUrl: string) {
    // This would error:
    // this.apiUrl = newUrl;

    // Instead, create a new instance:
    return new Config(newUrl, this.timeout);
  }
}

Readonly properties in classes can only be assigned in the constructor.

Advanced notes

### Understanding Readonly at Runtime

The readonly modifier is purely a TypeScript compile-time check. Once your TypeScript code compiles to JavaScript, there is no runtime enforcement of readonly constraints:

typescript
// TypeScript (with readonly)
interface User {
  readonly id: number;
}

const user: User = { id: 1 };
user.id = 2; // TS2540 error
javascript
// Compiled JavaScript (no readonly concept exists)
const user = { id: 1 };
user.id = 2; // Actually runs fine!

This is why using type assertions to bypass readonly can be dangerous—TypeScript won't prevent it at compile time, but your code will still mutate the object at runtime, potentially breaking logic that depends on immutability.

### Readonly Arrays and Tuples

The readonly modifier also applies to array types:

typescript
const readonlyArray: readonly string[] = ["a", "b", "c"];
readonlyArray.push("d");  // Error: Property 'push' does not exist on type 'readonly string[]'

const readonlyTuple: readonly [number, string] = [1, "a"];
readonlyTuple[0] = 2;     // Error: Cannot assign to '0' because it is a read-only property

To work with readonly arrays, you must create new arrays instead of mutating them.

### Readonly vs Object.freeze()

Don't confuse TypeScript's readonly with JavaScript's Object.freeze():

- readonly: TypeScript compile-time check; no runtime effect
- Object.freeze(): Runtime enforcement; makes objects truly immutable

For true immutability, combine both:

typescript
interface User {
  readonly id: number;
  readonly name: string;
}

const user: Readonly<User> = Object.freeze({ id: 1, name: "John" });
// Now it's both type-safe (readonly) and runtime-protected (frozen)

### Generic Readonly Utilities

TypeScript's built-in utilities include:

- Readonly<T>: Makes all properties readonly
- ReadonlyArray<T>: Creates readonly arrays
- ReadonlyMap, ReadonlySet: Collections with readonly interfaces

Use these when designing APIs that need immutability guarantees.

Frequently asked questions

What does "TS2540" mean?
In TypeScript, the readonly keyword restricts properties from being assigned after their initial declaration. This is a compile-time safety feature that helps prevent unintended mutations. When you mark a property as readonly, TypeScript enforces that it can only be assigned: 1.
How do I fix "TS2540"?
This TypeScript error occurs when you try to modify a property marked with the readonly modifier. The fix involves either removing the readonly modifier, creating a new object instead of mutating, or using a type utility to strip readonly from properties.
What causes "TS2540"?
Property is declared with the readonly modifier (most common) Property comes from a readonly interface or type Class property is declared as readonly and you're trying to modify it outside the constructor
Is the "TS2540" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading