AphrodyBun GitHub

Runtime docs · Linter · Rules

@typescript-eslint/prefer-readonly-parameter-types

Require the types of function parameters to be read-only

A function that sorts, pushes to, or assigns into its argument changes the caller's data. The caller can't see that from the call. If the parameter has a read-only type, TypeScript rejects those changes in the function body, and the signature promises the caller that the value comes back unchanged.

The check is deep, so a readonly array of objects that can be changed is still reported. Strings, numbers, and other primitives are always fine, and so are functions.

Configure

Off by default. This rule needs types, so turning it on makes bun lint type check your code first.

bunfig.toml
[lint.rules]
"@typescript-eslint/prefer-readonly-parameter-types" = "error" # or "warn"

Every option, set to its default:

bunfig.toml
[lint.rules]
"@typescript-eslint/prefer-readonly-parameter-types" = ["error", {
  allow = [], # list of strings or objects
  checkParameterProperties = true,
  ignoreInferredTypes = false,
  treatMethodsAsReadonly = false,
}]

Examples

Reported:

function total(prices: number[]) {}

function rename(user: { name: string }) {}

function names(users: readonly { name: string }[]) {}

Not reported:

function total(prices: readonly number[]) {}

function rename(user: Readonly<{ name: string }>) {}

function names(users: readonly { readonly name: string }[]) {}

function repeat(text: string, times: number, done: () => void) {}

Options

OptionDefaultWhat it does
allow[]Types that are accepted as they are.
checkParameterPropertiestrueCheck constructor parameters that declare a field, such as private items: string[].
ignoreInferredTypesfalseSkip parameters that have no type annotation.
treatMethodsAsReadonlyfalseAccept methods that aren't marked readonly.

Each allow entry is a type name, or an object that also says where the type comes from: { "from": "file", "name": "Draft" }, { "from": "lib", "name": "Date" }, or { "from": "package", "name": "Draft", "package": "some-package" }.

A Date is reported by default:

function format(date: Date) {}

With allow: [{ "from": "lib", "name": "Date" }] it's accepted:

function format(date: Date) {}

A callback's parameter often gets its type from the function you pass the callback to, which you might not control. Here row is a string[], so it's reported:

const rows = [["a", "b"], ["c"]];
rows.forEach(row => console.log(row.length));

With ignoreInferredTypes: true it's skipped:

const rows = [["a", "b"], ["c"]];
rows.forEach(row => console.log(row.length));

A method written as save(): void can be assigned to, so it counts as something that can change:

interface Store {
  save(): void;
}

function flush(store: Store) {}

With treatMethodsAsReadonly: true it doesn't:

interface Store {
  save(): void;
}

function flush(store: Store) {}

When not to use it

This rule is strict. Most library and DOM types aren't read-only, and every function that takes one is reported until you list the type under allow.