Skip to content

Commit e011e90

Browse files
a-tarasyukbradzacher
authored andcommitted
fix(eslint-plugin): [prefer-readonly] add handling for destructuring assignments
1 parent e01dc5f commit e011e90

File tree

2 files changed

+78
-1
lines changed

2 files changed

+78
-1
lines changed

packages/eslint-plugin/src/rules/prefer-readonly.ts

+30-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export default util.createRule<Options, MessageIds>({
6868
return;
6969
}
7070

71-
if (ts.isDeleteExpression(parent)) {
71+
if (ts.isDeleteExpression(parent) || isDestructuringAssignment(node)) {
7272
classScope.addVariableModification(node);
7373
return;
7474
}
@@ -108,6 +108,35 @@ export default util.createRule<Options, MessageIds>({
108108
}
109109
}
110110

111+
function isDestructuringAssignment(
112+
node: ts.PropertyAccessExpression,
113+
): boolean {
114+
let current: ts.Node = node.parent;
115+
116+
while (current) {
117+
const parent = current.parent;
118+
119+
if (
120+
ts.isObjectLiteralExpression(parent) ||
121+
ts.isArrayLiteralExpression(parent) ||
122+
ts.isSpreadAssignment(parent) ||
123+
(ts.isSpreadElement(parent) &&
124+
ts.isArrayLiteralExpression(parent.parent))
125+
) {
126+
current = parent;
127+
} else if (ts.isBinaryExpression(parent)) {
128+
return (
129+
parent.left === current &&
130+
parent.operatorToken.kind === ts.SyntaxKind.EqualsToken
131+
);
132+
} else {
133+
break;
134+
}
135+
}
136+
137+
return false;
138+
}
139+
111140
function isConstructor(node: TSESTree.Node): boolean {
112141
return (
113142
node.type === AST_NODE_TYPES.MethodDefinition &&

packages/eslint-plugin/tests/rules/prefer-readonly.test.ts

+48
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,54 @@ ruleTester.run('prefer-readonly', rule, {
220220
this['computed'] = 1;
221221
}
222222
}`,
223+
{
224+
code: `
225+
class Foo {
226+
private value: number = 0
227+
228+
bar(newValue: { value: number }) {
229+
({ value: this.value } = newValue);
230+
return this.value;
231+
}
232+
}
233+
`,
234+
},
235+
{
236+
code: `
237+
class Foo {
238+
private value: Record<string, number> = {};
239+
240+
bar(newValue: Record<string, number>) {
241+
({ ...this.value } = newValue);
242+
return this.value;
243+
}
244+
}
245+
`,
246+
},
247+
{
248+
code: `
249+
class Foo {
250+
private value: number[] = []
251+
252+
bar(newValue: number[]) {
253+
[...this.value] = newValue;
254+
return this.value;
255+
}
256+
}
257+
`,
258+
},
259+
{
260+
code: `
261+
class Foo {
262+
private value: number = 0;
263+
264+
bar(newValue: number[]) {
265+
[this.value] = newValue;
266+
return this.value;
267+
}
268+
}
269+
`,
270+
},
223271
],
224272
invalid: [
225273
{

0 commit comments

Comments
 (0)