Skip to content

Commit 50e789a

Browse files
fix(immutable-data): ignore casting when evaluating the expressions (#792)
fix #790
1 parent 1c1f086 commit 50e789a

File tree

7 files changed

+79
-19
lines changed

7 files changed

+79
-19
lines changed

src/rules/immutable-data.ts

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import {
4040
isMemberExpression,
4141
isNewExpression,
4242
isObjectConstructorType,
43+
isTSAsExpression,
4344
} from "#eslint-plugin-functional/utils/type-guards";
4445

4546
/**
@@ -361,62 +362,66 @@ function checkUpdateExpression(
361362
* a mutator method call.
362363
*/
363364
function isInChainCallAndFollowsNew(
364-
node: TSESTree.MemberExpression,
365+
node: TSESTree.Expression,
365366
context: Readonly<RuleContext<keyof typeof errorMessages, Options>>,
366367
): boolean {
368+
if (isMemberExpression(node)) {
369+
return isInChainCallAndFollowsNew(node.object, context);
370+
}
371+
372+
if (isTSAsExpression(node)) {
373+
return isInChainCallAndFollowsNew(node.expression, context);
374+
}
375+
367376
// Check for: [0, 1, 2]
368-
if (isArrayExpression(node.object)) {
377+
if (isArrayExpression(node)) {
369378
return true;
370379
}
371380

372381
// Check for: new Array()
373382
if (
374-
isNewExpression(node.object) &&
375-
isArrayConstructorType(getTypeOfNode(node.object.callee, context))
383+
isNewExpression(node) &&
384+
isArrayConstructorType(getTypeOfNode(node.callee, context))
376385
) {
377386
return true;
378387
}
379388

380389
if (
381-
isCallExpression(node.object) &&
382-
isMemberExpression(node.object.callee) &&
383-
isIdentifier(node.object.callee.property)
390+
isCallExpression(node) &&
391+
isMemberExpression(node.callee) &&
392+
isIdentifier(node.callee.property)
384393
) {
385394
// Check for: Array.from(iterable)
386395
if (
387-
arrayConstructorFunctions.some(
388-
isExpected(node.object.callee.property.name),
389-
) &&
390-
isArrayConstructorType(getTypeOfNode(node.object.callee.object, context))
396+
arrayConstructorFunctions.some(isExpected(node.callee.property.name)) &&
397+
isArrayConstructorType(getTypeOfNode(node.callee.object, context))
391398
) {
392399
return true;
393400
}
394401

395402
// Check for: array.slice(0)
396403
if (
397-
arrayNewObjectReturningMethods.some(
398-
isExpected(node.object.callee.property.name),
399-
)
404+
arrayNewObjectReturningMethods.some(isExpected(node.callee.property.name))
400405
) {
401406
return true;
402407
}
403408

404409
// Check for: Object.entries(object)
405410
if (
406411
objectConstructorNewObjectReturningMethods.some(
407-
isExpected(node.object.callee.property.name),
412+
isExpected(node.callee.property.name),
408413
) &&
409-
isObjectConstructorType(getTypeOfNode(node.object.callee.object, context))
414+
isObjectConstructorType(getTypeOfNode(node.callee.object, context))
410415
) {
411416
return true;
412417
}
413418

414419
// Check for: "".split("")
415420
if (
416421
stringConstructorNewObjectReturningMethods.some(
417-
isExpected(node.object.callee.property.name),
422+
isExpected(node.callee.property.name),
418423
) &&
419-
getTypeOfNode(node.object.callee.object, context).isStringLiteral()
424+
getTypeOfNode(node.callee.object, context).isStringLiteral()
420425
) {
421426
return true;
422427
}

src/utils/misc.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
isMemberExpression,
1313
isPrivateIdentifier,
1414
isThisExpression,
15+
isTSAsExpression,
1516
isTSTypeAnnotation,
1617
isUnaryExpression,
1718
isVariableDeclaration,
@@ -72,7 +73,9 @@ function getNodeIdentifierText(
7273
? context.sourceCode
7374
.getText(node.typeAnnotation as TSESTree.Node)
7475
.replaceAll(/\s+/gmu, "")
75-
: null;
76+
: isTSAsExpression(node)
77+
? getNodeIdentifierText(node.expression, context)
78+
: null;
7679

7780
if (identifierText !== null) {
7881
return identifierText;

src/utils/type-guards.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,12 @@ export function isTSArrayType(
237237
return node.type === AST_NODE_TYPES.TSArrayType;
238238
}
239239

240+
export function isTSAsExpression(
241+
node: TSESTree.Node,
242+
): node is TSESTree.TSAsExpression {
243+
return node.type === AST_NODE_TYPES.TSAsExpression;
244+
}
245+
240246
export function isTSFunctionType(
241247
node: TSESTree.Node,
242248
): node is TSESTree.TSFunctionType {

tests/rules/immutable-data/ts/array/invalid.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,20 @@ const tests: Array<
795795
},
796796
],
797797
},
798+
{
799+
code: dedent`
800+
(mutable_foo as string[]).sort();
801+
`,
802+
optionsSet: [[]],
803+
errors: [
804+
{
805+
messageId: "array",
806+
type: AST_NODE_TYPES.CallExpression,
807+
line: 1,
808+
column: 1,
809+
},
810+
],
811+
},
798812
];
799813

800814
export default tests;

tests/rules/immutable-data/ts/array/valid.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,18 @@ const tests: Array<ValidTestCaseSet<OptionsOf<typeof rule>>> = [
338338
`,
339339
optionsSet: [[{ ignoreImmediateMutation: true }]],
340340
},
341+
{
342+
code: dedent`
343+
(mutable_foo as string[]).sort();
344+
`,
345+
optionsSet: [[{ ignoreAccessorPattern: "mutable*" }]],
346+
},
347+
{
348+
code: dedent`
349+
([a, b, c] as string[]).sort();
350+
`,
351+
optionsSet: [[{ ignoreImmediateMutation: true }]],
352+
},
341353
];
342354

343355
export default tests;

tests/rules/immutable-data/ts/object/invalid.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,20 @@ const tests: Array<
511511
},
512512
],
513513
},
514+
{
515+
code: dedent`
516+
(mutable_foo as Bar).baz = "hello world";
517+
`,
518+
optionsSet: [[]],
519+
errors: [
520+
{
521+
messageId: "generic",
522+
type: AST_NODE_TYPES.AssignmentExpression,
523+
line: 1,
524+
column: 1,
525+
},
526+
],
527+
},
514528
];
515529

516530
export default tests;

tests/rules/immutable-data/ts/object/valid.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,12 @@ const tests: Array<ValidTestCaseSet<OptionsOf<typeof rule>>> = [
195195
`,
196196
optionsSet: [[{ ignoreNonConstDeclarations: true }]],
197197
},
198+
{
199+
code: dedent`
200+
(mutable_foo as Bar).baz = "hello world";
201+
`,
202+
optionsSet: [[{ ignoreAccessorPattern: "mutable*.*" }]],
203+
},
198204
];
199205

200206
export default tests;

0 commit comments

Comments
 (0)