Description
Suggestion
A new rule to prevent assignment of one value to another where the target has a lower immutability level (per https://github.com/RebeccaStevens/is-immutable-type/#definitions) than the source.
Such assignments can lead to surprising mutations in the source value.
For example, we might flag this as an issue:
export type ImmutableShallow<T extends {}> = {
readonly [P in keyof T & {}]: T[P];
};
// An immutable array
const foo: ImmutableShallow<ReadonlyArray<string>> = ["a", "b", "c"];
// A deep readonly array
// This assignment from Immutable to DeepReadonly is what the suggested rule would flag
const bar: ReadonlyArray<string> = foo;
// This compiles because bar is only deep readonly
bar.find = () => "WHOOPS";
// logs "WHOOPS" - we accidentally mutated a seemingly immutable value
console.log(foo.find((a) => a.length > 0));
That example uses an assignment expression, but the rule would ideally target method passing, return values, etc.
And of course that example uses Immutable and DeepReadonly, but it would ideally catch any step downwards down that hierarchy.
Strictly speaking, assignments in the other direction are unsafe too (can lead to later surprising mutation in the immutable value that was assigned to). Anything that leads to two handles to the same value where those handles have different opinions about mutability will lead to the issue, but in practice its the downward direction assignments that cause the most trouble imo.
Some tentative stabs in that direction: