This rule prohibits syntax that mutates existing objects and arrays via assignment to or deletion of their properties/elements.
While requiring the readonly
modifier forces declared types to be immutable,
it won't stop assignment into or modification of untyped objects or external types declared under different rules.
Examples of incorrect code for this rule:
/* eslint functional/immutable-data: "error" */
const obj = { foo: 1 };
obj.foo += 2; // <- Modifying an existing object/array is not allowed.
obj.bar = 1; // <- Modifying an existing object/array is not allowed.
delete obj.foo; // <- Modifying an existing object/array is not allowed.
Object.assign(obj, { bar: 2 }); // <- Modifying properties of existing object not allowed.
/* eslint functional/immutable-data: "error" */
const arr = [0, 1, 2];
arr[0] = 4; // <- Modifying an array is not allowed.
arr.length = 1; // <- Modifying an array is not allowed.
arr.push(3); // <- Modifying an array is not allowed.
Examples of correct code for this rule:
/* eslint functional/immutable-data: "error" */
const obj = { foo: 1 };
const arr = [0, 1, 2];
const x = {
...obj
bar: [
...arr, 3, 4
]
}
This rule accepts an options object of the following type:
{
assumeTypes:
| boolean
| {
forArrays: boolean;
forObjects: boolean;
}
ignoreClass: boolean | "fieldsOnly";
ignoreImmediateMutation: boolean;
ignorePattern?: string | Array<string>;
ignoreAccessorPattern?: string | Array<string>;
}
The default options:
{
assumeTypes: true,
ignoreClass: false,
ignoreImmediateMutation: true,
};
Note: the lite
ruleset overrides the default options to:
{
assumeTypes: true,
ignoreClass: "fieldsOnly",
ignoreImmediateMutation: true,
}
The rule take advantage of TypeScript's typing engine to check if mutation is taking place. If you are not using TypeScript, type checking cannot be performed; hence this option exists.
This option will make the rule assume the type of the nodes it is checking are of type Array/Object. However this may result in some false positives being picked up.
Disabling this option can result in false negatives, for example:
// When this option is DISABLED (and type info is not available).
const x = [0, 1, 2];
x.push(3); // This will NOT be flagged.
// This is due to the fact that without a typing engine, we cannot tell that x is an array.
Note: This option will have no effect if the TypeScript typing engine is available (i.e. you are using TypeScript and have configured ESLint correctly).
If true, immediate mutation of objects before they are assigned to a variable is allowed. This allows for the use of array mutator methods to be chained to newly created arrays.
For example, an array can be immutably sorted like so:
const original = ["foo", "bar", "baz"];
const sorted = [...original].sort((a, b) => a.localeCompare(b)); // This is OK with ignoreImmediateMutation.
Ignore mutations inside classes.
Classes already aren't functional so ignore mutations going on inside them.
Patterns will be matched against variable names. See the ignorePattern docs for more information.
This option takes a match string or an array of match strings (not a RegExp pattern).
The match string allows you to specify dot separated .
object paths and has support for "glob" *
and "globstar" **
matching.
For example:
{
// Ignore all reassigning to object properties that are prefixed with "mutable_".
"ignoreAccessorPattern": "**.mutable_*"
}
{
// Ignore all shallow mutations made to object properties that are prefixed with "mutable_".
"ignoreAccessorPattern": "**.mutable_*.*"
}
{
// Ignore all deep mutations made to object properties that are prefixed with "mutable_".
"ignoreAccessorPattern": "**.mutable_*.*.**"
}
{
// Ignore all deep mutations and reassigning to object properties that are prefixed with "mutable_".
"ignoreAccessorPattern": "**.mutable_*.**"
// This is the same as `"ignoreAccessorPattern": ["**.mutable_*", "**.mutable_*.*.**"]`
}
The following wildcards can be used when specifying a pattern:
**
- Match any depth (including zero). Can only be used as a full accessor.
*
- When used as a full accessor, match the next accessor (there must be one). When used as part of an accessor, match any characters.