This rule enforces use of readonly type declarations.
This rule checks that declared types are deeply readonly (unless declared to not be).
It can also be used to enforce a naming convention for readonly vs mutable type aliases and interfaces.
Examples of incorrect code for this rule:
/* eslint functional/prefer-readonly-type-declaration: "error" */
type Point = {
x: number;
y: number;
};
const point: Point = { x: 23, y: 44 };
point.x = 99;
Examples of correct code for this rule:
/* eslint functional/prefer-readonly-type-declaration: "error" */
type Point = {
readonly x: number;
readonly y: number;
};
const point1: Point = { x: 23, y: 44 };
const transformedPoint1 = { ...point, x: 99 };
type MutablePoint = {
x: number;
y: number;
};
const point2: MutablePoint = { x: 23, y: 44 };
point2.x = 99;
This rule accepts an options object of the following type:
{
allowLocalMutation: boolean;
allowMutableReturnType: boolean;
ignoreClass: boolean | "fieldsOnly";
ignoreInterface: boolean;
ignoreCollections: boolean;
ignorePattern?: string | Array<string>;
aliases: {
mustBeReadonly: {
pattern: ReadonlyArray<string> | string;
requireOthersToBeMutable: boolean;
};
mustBeMutable: {
pattern: ReadonlyArray<string> | string;
requireOthersToBeReadonly: boolean;
};
blacklist: ReadonlyArray<string> | string;
};
}
The default options:
{
allowLocalMutation: false,
allowMutableReturnType: true,
ignoreClass: false,
ignoreInterface: false,
ignoreCollections: false,
aliases: {
blacklist: "^Mutable$",
mustBeReadonly: {
pattern: "^(I?)Readonly",
requireOthersToBeMutable: false,
},
mustBeMutable: {
pattern: "^(I?)Mutable",
requireOthersToBeReadonly: true,
},
},
}
If set, return types of functions will not be checked.
If set, classes will not be checked.
Examples of incorrect code for the { "ignoreClass": false }
option:
/* eslint functional/readonly: ["error", { "ignoreClass": false }] */
class {
myprop: string;
}
Examples of correct code for the { "ignoreClass": true }
option:
/* eslint functional/readonly: ["error", { "ignoreClass": true }] */
class {
myprop: string;
}
If set, interfaces will not be checked.
Examples of incorrect code for the { "ignoreInterface": false }
option:
/* eslint functional/readonly: ["error", { "ignoreInterface": false }] */
interface {
myprop: string;
}
Examples of correct code for the { "ignoreInterface": true }
option:
/* eslint functional/readonly: ["error", { "ignoreInterface": true }] */
interface {
myprop: string;
}
If set, collections (Array, Tuple, Set, and Map) will not be required to be readonly when used outside of type aliases and interfaces.
Examples of incorrect code for the { "ignoreCollections": false }
option:
/* eslint functional/readonly: ["error", { "ignoreCollections": false }] */
const foo: number[] = [];
const bar: [string, string] = ["foo", "bar"];
const baz: Set<string, string> = new Set();
const qux: Map<string, string> = new Map();
Examples of correct code for the { "ignoreCollections": true }
option:
/* eslint functional/readonly: ["error", { "ignoreCollections": true }] */
const foo: number[] = [];
const bar: [string, string] = ["foo", "bar"];
const baz: Set<string, string> = new Set();
const qux: Map<string, string> = new Map();
These options apply only to type aliases and interface declarations.
The regex pattern(s) used to test against the type's name. If it's a match the type must be deeply readonly.
Set to an empty array to disable this check.
If set, all other types that don't match the pattern(s) must not be deeply readonly.
The regex pattern(s) used to test against the type's name. If it's a match the type must not be deeply readonly.
Set to an empty array to disable this check.
If set, all other types that don't match the pattern(s) must be deeply readonly.
Any type names that match this regex pattern(s) will be ignored by this rule.
By toggling the default settings of aliases.mustBeReadonly.requireOthersToBeMutable
and aliases.mustBeMutable.requireOthersToBeReadonly
, you can make it so that types are mutable by default and immutable versions need to be prefixed. This more closely matches how TypeScript itself implements types like Set
and ReadonlySet
.
/* eslint functional/prefer-readonly-type-declaration: ["error", { "aliases": { "mustBeReadonly": { "requireOthersToBeMutable": true }, "mustBeMutable": { "requireOthersToBeReadonly": false } } }] */
type Point = {
x: number;
y: number;
};
type ReadonlyPoint = Readonly<Point>;
Alternatively, if both aliases.mustBeReadonly.requireOthersToBeMutable
and aliases.mustBeMutable.requireOthersToBeReadonly
are set, you can make it so that types explicitly need to be marked as either readonly or mutable.
/* eslint functional/prefer-readonly-type-declaration: ["error", { "aliases": { "mustBeReadonly": { "requireOthersToBeMutable": true }, "mustBeMutable": { "requireOthersToBeReadonly": true } } }] */
type MutablePoint = {
x: number;
y: number;
};
type ReadonlyPoint = Readonly<MutablePoint>;
See the allowLocalMutation docs.
See the ignorePattern docs.