How to fix Cannot use 'readonly' modifier in mapped type without keyof in TypeScript

TypeScriptINTERMEDIATELOWUpdated Dec 11, 2025· 129 views

This TypeScript error occurs when trying to use the readonly modifier in a mapped type without properly using the keyof operator. Mapped types require keyof to iterate over object keys when applying modifiers like readonly. The fix involves correctly structuring mapped types with keyof.

Quick fix

Fixes Cannot use 'readonly' modifier in mapped type without keyof

// ❌ Incorrect - causes the error
type ReadonlyObject = {
  readonly [K in string]: any;
};

// ✅ Correct - uses keyof with a source type
type Readonly<T> = {
  readonly [K in keyof T]: T[K];
};

// Example usage
interface User {
  name: string;
  age: number;
}

type ReadonlyUser = Readonly<User>;
// Equivalent to: { readonly name: string; readonly age: number; }
  1. Understand the correct mapped type syntax for readonly
  2. Fix existing incorrect mapped type definitions
  3. Use built-in Readonly utility type
  4. Create custom readonly mapped types for specific use cases
typescript — error
// ❌ Incorrect - causes the error
type ReadonlyObject = {
readonly [K in string]: any;
};
// ✅ Correct - uses keyof with a source type
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};
// Example usage
interface User {
name: string;
age: number;
}
type ReadonlyUser = Readonly<User>;
// Equivalent to: { readonly name: string; readonly age: number; }
Cannot use 'readonly' modifier in mapped type without keyof

What this error means

The error "Cannot use 'readonly' modifier in mapped type without keyof" occurs in TypeScript when you attempt to create a mapped type that applies the readonly modifier without properly using the keyof type operator. Mapped types in TypeScript allow you to create new types by transforming properties of existing types. When you want to make all properties of an object type readonly, you need to use a mapped type that iterates over the keys of the original type using keyof. The error happens because TypeScript expects mapped types that apply property modifiers (like readonly or ? for optional) to explicitly iterate over keys using the keyof operator. This ensures type safety and clarity about which properties are being transformed.

Typical symptoms

  • TypeScript compiler shows error: "Cannot use 'readonly' modifier in mapped type without keyof"
  • Code fails to compile when trying to create readonly versions of object types
  • Mapped type definitions with readonly modifier cause compilation errors
  • Attempts to make object properties immutable fail at compile time

Common causes

  1. Using readonly modifier directly in a mapped type without keyof iteration
  2. Incorrect mapped type syntax when trying to make properties readonly
  3. Missing keyof operator in mapped type definition
  4. Confusion between mapped types and regular type aliases

How to fix "Cannot use 'readonly' modifier in mapped type without keyof"

1Understand the correct mapped type syntax for readonly

The correct syntax for creating a readonly version of an object type uses keyof to iterate over the keys:

typescript
// ❌ Incorrect - causes the error
type ReadonlyObject = {
  readonly [K in string]: any;
};

// ✅ Correct - uses keyof with a source type
type Readonly<T> = {
  readonly [K in keyof T]: T[K];
};

// Example usage
interface User {
  name: string;
  age: number;
}

type ReadonlyUser = Readonly<User>;
// Equivalent to: { readonly name: string; readonly age: number; }
2Fix existing incorrect mapped type definitions

If you have code causing this error, update it to use the correct syntax:

typescript
// Before - causes error
type MyReadonlyType = {
  readonly [K in 'prop1' | 'prop2']: string;
};

// After - fixed with proper mapped type
type Keys = 'prop1' | 'prop2';
type MyReadonlyType = {
  readonly [K in Keys]: string;
};

// Or using a helper type
type MakeReadonly<T extends Record<string, any>> = {
  readonly [K in keyof T]: T[K];
};
3Use built-in Readonly utility type

TypeScript provides a built-in Readonly<T> utility type that handles this correctly:

typescript
interface Config {
  apiUrl: string;
  timeout: number;
  retries: number;
}

// Use the built-in Readonly utility type
type ReadonlyConfig = Readonly<Config>;
// Equivalent to: {
//   readonly apiUrl: string;
//   readonly timeout: number;
//   readonly retries: number;
// }

// You can also create partial readonly types
type PartiallyReadonly<T, K extends keyof T> = Readonly<Pick<T, K>> & Omit<T, K>;

type ConfigWithReadonlyApi = PartiallyReadonly<Config, 'apiUrl'>;
// apiUrl is readonly, timeout and retries are mutable
4Create custom readonly mapped types for specific use cases

For advanced use cases, create custom mapped types with readonly:

typescript
// Deep readonly - makes nested objects readonly too
type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object
    ? DeepReadonly<T[K]>
    : T[K];
};

// Readonly except for specific keys
type ReadonlyExcept<T, K extends keyof T> = {
  readonly [P in Exclude<keyof T, K>]: T[P];
} & {
  [P in K]: T[P];
};

interface State {
  count: number;
  user: { name: string; email: string };
  timestamp: Date;
}

type ReadonlyState = DeepReadonly<State>;
// All properties including nested 'user' object are readonly

type StateWithMutableCount = ReadonlyExcept<State, 'count'>;
// Only 'count' is mutable, others are readonly
5Test your readonly types work correctly

Verify that your readonly types prevent mutation:

typescript
interface Product {
  id: string;
  name: string;
  price: number;
  details: {
    category: string;
    inStock: boolean;
  };
}

type ReadonlyProduct = Readonly<Product>;

const product: ReadonlyProduct = {
  id: '123',
  name: 'Widget',
  price: 29.99,
  details: { category: 'Tools', inStock: true }
};

// These will cause TypeScript errors:
// product.name = 'New Name'; // Error: Cannot assign to 'name' because it is a read-only property
// product.details.category = 'New Category'; // Error with shallow Readonly
// product.details = { category: 'New', inStock: false }; // Error

// For deep readonly:
type DeepReadonlyProduct = DeepReadonly<Product>;
const deepProduct: DeepReadonlyProduct = { ...product };

// This will also error:
// deepProduct.details.category = 'New Category'; // Error with DeepReadonly

Advanced notes

## Understanding Mapped Type Modifiers

TypeScript mapped types support two kinds of modifiers:
1. Readonly modifier (readonly): Makes properties immutable
2. Optionality modifier (?): Makes properties optional

You can also remove modifiers using -readonly and -?:

typescript
// Remove readonly from a type
type Mutable<T> = {
  -readonly [K in keyof T]: T[K];
};

// Remove optional modifier
type Required<T> = {
  [K in keyof T]-?: T[K];
};

## Common Pitfalls

1. Missing keyof: Always use keyof T when applying modifiers in mapped types
2. Shallow vs Deep: Built-in Readonly<T> only makes top-level properties readonly
3. Performance: Complex mapped types with many transformations can slow down type checking
4. Circular references: Avoid creating mapped types that reference themselves

## Real-World Use Cases

- Immutable state in Redux or state management libraries
- Configuration objects that shouldn't be modified after initialization
- API response types where data should be treated as immutable
- Library APIs that return readonly data structures

Frequently asked questions

What does "Cannot use 'readonly' modifier in mapped type without keyof" mean?
The error "Cannot use 'readonly' modifier in mapped type without keyof" occurs in TypeScript when you attempt to create a mapped type that applies the readonly modifier without properly using the keyof type operator. Mapped types in TypeScript allow you to create new types by transforming properties of existing types. When you want to make all properties of an object type readonly, you need to use a mapped type that iterates over the keys of the original type using keyof.
How do I fix "Cannot use 'readonly' modifier in mapped type without keyof"?
This TypeScript error occurs when trying to use the readonly modifier in a mapped type without properly using the keyof operator. Mapped types require keyof to iterate over object keys when applying modifiers like readonly. The fix involves correctly structuring mapped types with keyof.
What causes "Cannot use 'readonly' modifier in mapped type without keyof"?
Using readonly modifier directly in a mapped type without keyof iteration Incorrect mapped type syntax when trying to make properties readonly Missing keyof operator in mapped type definition
Is the "Cannot use 'readonly' modifier in mapped type without keyof" error serious?
It is a low-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading