TypeScript error

TS2540: Assigning to a read-only property

  • Error TS2540
  • Tested with TypeScript 5.9

error TS2540: Cannot assign to 'id' because it is a read-only property. is reported when code writes to a property whose type says it cannot change. In JavaScript projects the read-only marking comes from three places: a @readonly tag on a class field, Object.freeze (whose result the checker types as Readonly), and Readonly<...> or readonly inside a JSDoc type.

For frozen objects the error matches what the running code does: in a module (always strict mode) assigning to a frozen property throws a TypeError. For @readonly fields it is a promise the checker keeps for you.

At a glance

CodeTS2540
MessageCannot assign to '{0}' because it is a read-only property.
TypeScript's categoryerror
Listed underClasses, JSX and unused code
Turned on by No option: every checked file reports it
In .js files Reported when the file is checked: checkJs in jsconfig.json, or // @ts-check at the top of the file

The message is TypeScript's own text from its diagnostic messages file (Apache-2.0); {0} is filled in by tsc for each error.

What it looks like

order.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: order.js(8,7): error TS2540: Cannot assign to 'id' because it is a read-only property. order.js(12,10): error TS2540: Cannot assign to 'express' because it is a read-only property. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. order.js(8,7): error TS2540: Cannot assign to 'id' because it is a read-only property. order.js(12,10): error TS2540: Cannot assign to 'express' because it is a read-only property. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. order.js(8,7): error TS2540: Cannot assign to 'id' because it is a read-only property. order.js(12,10): error TS2540: Cannot assign to 'express' because it is a read-only property. checked 21 October 2026
export class Order {
  /** @readonly */
  id = crypto.randomUUID();
}

const order = new Order();
order.id = "custom-id";error TS2540: Cannot assign to 'id' because it is a read-only property.

const SHIPPING = Object.freeze({ standard: 4, express: 12 });
SHIPPING.express = 10;error TS2540: Cannot assign to 'express' because it is a read-only property.
Assigning to a @readonly field and to a frozen object.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

What causes it

  • A @readonly field is assigned outside the constructor.
  • A property of an Object.freeze(...) result is assigned.
  • A value typed Readonly<T> (or with a readonly property) is modified.

How to fix it

Set it in the constructor, or make a changed copy

A @readonly field may be assigned in the class's constructor, so values that differ per instance go in as constructor arguments. For frozen data, build a new object with spread, { ...SHIPPING, express: 10 }, and leave the original alone.

order.js TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026: No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. No errors. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. No errors. checked 21 October 2026
export class Order {
  /** @readonly */
  id;

  /** @param {string} [id] */
  constructor(id = crypto.randomUUID()) {
    this.id = id;
  }
}

const order = new Order("custom-id");
console.log(order.id);

const SHIPPING = Object.freeze({ standard: 4, express: 12 });
export const holidayShipping = { ...SHIPPING, express: 10 };
Make a new object instead of changing the read-only one.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

Gotchas

  • Read-only is shallow: Object.freeze and Readonly protect the top-level properties, not the objects inside them.
  • Arrays have their own spelling, readonly string[] or ReadonlyArray<string>, which removes push and the other mutating methods; see read-only arrays.

Sources

Last reviewed 20 October 2026.