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.