Skip to main content

typescript/prefer-readonly-record

Require Readonly<Record<K, V>> instead of mutable Record<K, V>.

Deprecated

Use only the scoped local rules for positions your project intends to govern. The typescript-eslint replacement performs deeper, type-aware parameter analysis. Do not enable this broad rule alongside those replacements because their diagnostics overlap.

Targeted pattern scope​

This rule targets Record type references in TypeScript type positions.

What this rule reports​

This rule reports Record<...> references that are not already wrapped by Readonly<...>.

Why this rule exists​

Record<K, V> is writable by default. Wrapping it with Readonly<...> makes immutability explicit in API and model types.

❌ Incorrect​

type Store = Record<string, number>;

type Nested = Promise<Record<string, Record<string, number>>>;

✅ Correct​

type Store = Readonly<Record<string, number>>;

type Nested = Promise<
Readonly<Record<string, Readonly<Record<string, number>>>>
>;

Behavior and migration notes​

This rule is autofixable. The fixer wraps each reported Record<...> with Readonly<...>.

Additional examples​

type Flags = Record<string, boolean>;
// ❌ reported

type FlagsView = Readonly<Record<string, boolean>>;
// ✅ valid

ESLint flat config example​

import etcMisc from "eslint-plugin-etc-misc";

export default [
{
plugins: { "etc-misc": etcMisc },
rules: {
"etc-misc/typescript/prefer-readonly-record": "error",
},
},
];

When not to use it​

Disable this rule if mutable Record types are a deliberate project convention.

Package documentation​

Rule catalog ID: R139

Further reading​

Adoption resources​

  • Start at warning level in CI, then move to error after cleanup.
  • Use focused codemods/autofix batches per package or directory.