How to fix Index signature in type 'X' only permits reading in TypeScript

TypeScriptINTERMEDIATEMEDIUMUpdated Dec 10, 2025· 105 views

This TypeScript error occurs when you try to write to an index signature marked as readonly. Index signatures allow dynamic property access, and when marked readonly, they prevent assignment to any property accessed via bracket notation. The fix involves either removing the readonly modifier, using mutable types, or creating new objects instead of mutating existing ones.

Quick fix

Fixes TS2542

// Example 1: Readonly array index signature
interface ReadonlyStringArray {
  readonly [index: number]: string;
}

const arr: ReadonlyStringArray = ["a", "b", "c"];
arr[0] = "x"; // Error: Index signature in type 'ReadonlyStringArray' only permits reading

// Example 2: Readonly object index signature
interface ReadonlyConfig {
  readonly [key: string]: string;
}

const config: ReadonlyConfig = { apiUrl: "https://api.example.com" };
config["apiUrl"] = "https://new-api.com"; // Error: Index signature only permits reading

// Example 3: Using ReadonlyArray<T>
const numbers: ReadonlyArray<number> = [1, 2, 3];
numbers[0] = 10; // Error: Index signature in type 'readonly number[]' only permits reading
  1. Identify the readonly index signature
  2. Remove readonly from the index signature (if appropriate)
  3. Use mutable array types instead of readonly arrays
  4. Create new objects/arrays instead of mutating (immutable approach)
typescript — error
// Example 1: Readonly array index signature
interface ReadonlyStringArray {
readonly [index: number]: string;
}
const arr: ReadonlyStringArray = ["a", "b", "c"];
arr[0] = "x"; // Error: Index signature in type 'ReadonlyStringArray' only permits reading
// Example 2: Readonly object index signature
interface ReadonlyConfig {
readonly [key: string]: string;
}
const config: ReadonlyConfig = { apiUrl: "https://api.example.com" };
config["apiUrl"] = "https://new-api.com"; // Error: Index signature only permits reading
// Example 3: Using ReadonlyArray<T>
const numbers: ReadonlyArray<number> = [1, 2, 3];
numbers[0] = 10; // Error: Index signature in type 'readonly number[]' only permits reading
Index signature in type 'X' only permits reading

What this error means

In TypeScript, index signatures allow you to define types for objects where you don't know all property names ahead of time. When you mark an index signature as `readonly`, you're telling TypeScript that all properties accessed via that index signature pattern should be read-only. The error "Index signature in type 'X' only permits reading" occurs when you try to assign a value to a property through a readonly index signature. This is TypeScript's way of enforcing immutability for dynamically accessed properties. For example, with `readonly [index: number]: string`, you can read array elements but cannot modify them. This applies to both numeric index signatures (arrays) and string index signatures (objects with dynamic keys).

Typical symptoms

  • TypeScript compiler shows 'Index signature in type 'X' only permits reading' error
  • You're trying to assign a value using bracket notation (obj[key] = value)
  • Error occurs when working with arrays or objects with dynamic keys
  • The interface or type has a readonly index signature
  • You're using ReadonlyArray<T> or readonly T[] types
  • Error mentions a specific type name where the index signature is defined
  • Assignment works in JavaScript but fails TypeScript type checking

Common causes

  1. Interface has a readonly index signature (e.g., readonly [key: string]: string)
  2. Using ReadonlyArray<T> type which has readonly [index: number]: T
  3. Array is typed as readonly T[] (shorthand for readonly arrays)
  4. Object type comes from a third-party library with readonly index signatures
  5. Using const assertion (as const) creates readonly types with readonly index signatures
  6. Mapped type like Readonly<T> adds readonly to all properties including index signatures
  7. Inheriting from a parent type with readonly index signatures

How to fix "Index signature in type 'X' only permits reading"

1Identify the readonly index signature

First, locate where the readonly index signature is defined. Look for interfaces or types with readonly [index: type]: valueType:

typescript
// Example 1: Readonly array index signature
interface ReadonlyStringArray {
  readonly [index: number]: string;
}

const arr: ReadonlyStringArray = ["a", "b", "c"];
arr[0] = "x"; // Error: Index signature in type 'ReadonlyStringArray' only permits reading

// Example 2: Readonly object index signature
interface ReadonlyConfig {
  readonly [key: string]: string;
}

const config: ReadonlyConfig = { apiUrl: "https://api.example.com" };
config["apiUrl"] = "https://new-api.com"; // Error: Index signature only permits reading

// Example 3: Using ReadonlyArray<T>
const numbers: ReadonlyArray<number> = [1, 2, 3];
numbers[0] = 10; // Error: Index signature in type 'readonly number[]' only permits reading

Check your type definitions, imported types, and any as const assertions that might create readonly types.

2Remove readonly from the index signature (if appropriate)

If you need to modify properties accessed via index signatures, remove the readonly keyword:

typescript
// Before (readonly - cannot assign)
interface Config {
  readonly [key: string]: string;
}

// After (mutable - can assign)
interface Config {
  [key: string]: string; // No readonly modifier
}

const config: Config = { apiUrl: "https://api.example.com" };
config["apiUrl"] = "https://new-api.com"; // Now OK
config.newKey = "value"; // Also OK

// For arrays:
// Before: readonly [index: number]: string
// After: [index: number]: string

Note: Only remove readonly if you genuinely need mutability. Readonly index signatures provide valuable immutability guarantees.

3Use mutable array types instead of readonly arrays

For arrays, use regular array types instead of readonly variants:

typescript
// Problem: Readonly array
const readonlyNumbers: readonly number[] = [1, 2, 3];
readonlyNumbers[0] = 10; // Error

// Solution 1: Regular array type
const mutableNumbers: number[] = [1, 2, 3];
mutableNumbers[0] = 10; // OK

// Solution 2: Array<T> instead of ReadonlyArray<T>
const numbersArray: Array<number> = [1, 2, 3];
numbersArray[0] = 10; // OK

// Solution 3: Remove ReadonlyArray wrapper
type ReadonlyUserArray = ReadonlyArray<{ id: number; name: string }>;
type UserArray = Array<{ id: number; name: string }>; // Mutable version

const users: UserArray = [{ id: 1, name: "John" }];
users[0].name = "Jane"; // OK

Remember that readonly T[] and ReadonlyArray<T> are equivalent and both prevent modification.

4Create new objects/arrays instead of mutating (immutable approach)

Instead of modifying readonly indexed properties, create new objects or arrays:

typescript
// For objects with readonly index signatures
interface ReadonlyConfig {
  readonly [key: string]: string;
}

const config: ReadonlyConfig = { apiUrl: "https://api.example.com" };

// Don't mutate; create a new object
const updatedConfig: ReadonlyConfig = {
  ...config,
  apiUrl: "https://new-api.com",
};

// For readonly arrays
const readonlyArray: readonly number[] = [1, 2, 3];

// Create new array with modifications
const newArray: readonly number[] = [
  10,          // New first element
  ...readonlyArray.slice(1) // Rest of original array
];

// Or use array methods that return new arrays
const doubledArray: readonly number[] = readonlyArray.map(n => n * 2);

This immutable approach is often better for predictability and avoids side effects.

5Use type utilities to strip readonly from index signatures

Create utility types to remove readonly from index signatures when needed:

typescript
// Utility to make any type mutable (removes readonly from properties and index signatures)
type Mutable<T> = {
  -readonly [P in keyof T]: T[P];
};

interface ReadonlyConfig {
  readonly [key: string]: string;
  readonly apiUrl: string;
}

// Create mutable version
const config: Mutable<ReadonlyConfig> = { apiUrl: "https://api.example.com" };
config["apiUrl"] = "https://new-api.com"; // Now OK
config.newKey = "value"; // Also OK

// For arrays specifically
type MutableArray<T> = T extends readonly (infer U)[] ? U[] : T;

const readonlyArr: readonly number[] = [1, 2, 3];
const mutableArr: MutableArray<typeof readonlyArr> = [...readonlyArr];
mutableArr[0] = 10; // OK

The -readonly mapped type modifier removes readonly constraints from both regular properties and index signatures.

6Use type assertions carefully as a last resort

If you must bypass the readonly check temporarily, use type assertions with caution:

typescript
interface ReadonlyConfig {
  readonly [key: string]: string;
}

const config: ReadonlyConfig = { apiUrl: "https://api.example.com" };

// Type assertion to bypass readonly (use sparingly!)
(config as { [key: string]: string })["apiUrl"] = "https://new-api.com";

// Or for arrays
const readonlyArray: readonly number[] = [1, 2, 3];
(readonlyArray as number[])[0] = 10;

// Even more permissive (not recommended)
(readonlyArray as any)[0] = 10;

Important warnings:
1. This bypasses TypeScript's safety checks
2. The code will still run and mutate the object/array
3. Can lead to bugs if other code depends on immutability
4. Only use when you're certain and there's no better alternative

Consider using Object.assign() or spread syntax to create modified copies instead.

7Handle const assertions (as const) properly

The as const assertion creates deeply readonly types, including readonly index signatures:

typescript
// as const makes everything readonly
const config = {
  apiUrl: "https://api.example.com",
  endpoints: ["/users", "/posts"],
} as const;

config.apiUrl = "https://new.com"; // Error: Cannot assign to 'apiUrl' because it is read-only
config["apiUrl"] = "https://new.com"; // Error: Index signature only permits reading
config.endpoints[0] = "/new"; // Error: Index signature in type 'readonly ["/users", "/posts"]' only permits reading

// Solutions:
// 1. Don't use as const if you need mutability
const mutableConfig = {
  apiUrl: "https://api.example.com",
  endpoints: ["/users", "/posts"],
}; // No as const

mutableConfig.apiUrl = "https://new.com"; // OK

// 2. Create mutable copy when needed
const readonlyConfig = { apiUrl: "https://api.example.com" } as const;
const mutableCopy = { ...readonlyConfig, apiUrl: "https://new.com" };

// 3. Use type utility to remove readonly
type Mutable<T> = { -readonly [P in keyof T]: T[P] };
const mutableVersion: Mutable<typeof readonlyConfig> = { apiUrl: "https://new.com" };

Remember: as const is for creating truly immutable constants.

Advanced notes

### Understanding Index Signatures

Index signatures in TypeScript come in two main forms:

1. String index signatures: [key: string]: ValueType
- Matches any string key
- All explicitly declared properties must match this type
- Example: { [key: string]: number; x: number; y: number }

2. Numeric index signatures: [index: number]: ValueType
- Matches array indices
- Used by array types
- Example: Array<T> has [index: number]: T

### Readonly Index Signatures vs Readonly Properties

There's an important distinction:
- Readonly property: readonly prop: string - only affects that specific property
- Readonly index signature: readonly [key: string]: string - affects ALL properties accessed via that index pattern

typescript
interface Example {
  readonly x: string;          // Only x is readonly
  readonly [key: string]: string; // ALL string-keyed properties are readonly
}

const obj: Example = { x: "a", y: "b", z: "c" };
obj.x = "new";     // Error: x is readonly property
obj["y"] = "new";  // Error: Index signature only permits reading
obj.z = "new";     // Error: Index signature only permits reading

### Runtime Implications

Like regular readonly properties, readonly index signatures are compile-time only. JavaScript has no concept of readonly index signatures:

typescript
// TypeScript (compile error)
interface ReadonlyObj {
  readonly [key: string]: string;
}
const obj: ReadonlyObj = { x: "a" };
obj["x"] = "b"; // TS2542: Index signature only permits reading
javascript
// Compiled JavaScript (runs fine)
const obj = { x: "a" };
obj["x"] = "b"; // Actually works!

This is why type assertions can be dangerous—they bypass TypeScript's checks but the mutation still happens at runtime.

### Built-in Readonly Types

TypeScript provides several built-in readonly types:

- Readonly<T>: Makes all properties (including index signatures) readonly
- ReadonlyArray<T>: Array with readonly [index: number]: T
- ReadonlyMap<K, V> and ReadonlySet<T>: Immutable collections
- readonly T[]: Shorthand for ReadonlyArray<T>

### Performance Considerations

While readonly index signatures provide safety, they don't affect runtime performance. The TypeScript compiler removes all readonly annotations during compilation.

### When to Use Readonly Index Signatures

Use readonly index signatures when:
1. Working with configuration objects that shouldn't change
2. Creating immutable data structures
3. Designing APIs that return frozen/immutable data
4. Preventing accidental mutations in shared state
5. Working with functional programming patterns

Avoid readonly index signatures when:
1. You need to modify object properties dynamically
2. Working with mutable state management
3. Performance-critical code where object creation is expensive
4. Legacy code that relies on mutation patterns

### Migration Strategy

If you're converting existing code to use readonly index signatures:
1. Start with readonly [key: string]: any to catch all assignments
2. Gradually tighten the value type
3. Use the "create new object" pattern for modifications
4. Update dependent code to work with immutable data

Frequently asked questions

What does "TS2542" mean?
In TypeScript, index signatures allow you to define types for objects where you don't know all property names ahead of time. When you mark an index signature as readonly, you're telling TypeScript that all properties accessed via that index signature pattern should be read-only. The error "Index signature in type 'X' only permits reading" occurs when you try to assign a value to a property through a readonly index signature.
How do I fix "TS2542"?
This TypeScript error occurs when you try to write to an index signature marked as readonly. Index signatures allow dynamic property access, and when marked readonly, they prevent assignment to any property accessed via bracket notation. The fix involves either removing the readonly modifier, using mutable types, or creating new objects instead of mutating existing ones.
What causes "TS2542"?
Interface has a readonly index signature (e.g., readonly [key: string]: string) Using ReadonlyArray<T> type which has readonly [index: number]: T Array is typed as readonly T[] (shorthand for readonly arrays)
Is the "TS2542" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading