Skip to content

fix(eslint-plugin): [consistent-indexed-object-style] fixed circular reference issue #2766

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

lakshyabatman
Copy link

@lakshyabatman lakshyabatman commented Nov 15, 2020

The logic checks if the value includes the node name it'll not trigger the rule.

Fixes #2687

@typescript-eslint
Copy link
Contributor

Thanks for the PR, @lakshyabatman!

typescript-eslint is a 100% community driven project, and we are incredibly grateful that you are contributing to that community.

The core maintainers work on this in their personal time, so please understand that it may not be possible for them to review your work immediately.

Thanks again!


🙏 Please, if you or your company is finding typescript-eslint valuable, help us sustain the project by sponsoring it transparently on https://opencollective.com/typescript-eslint. As a thank you, your profile/company logo will be added to our main README which receives thousands of unique visitors per day.

Comment on lines 97 to 101
const value = sourceCode.getText(valueType.typeAnnotation);
const name = node.parent?.id?.name;
if (value.includes(name)) {
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello! Thank you for the PR for my issue. I do not have experience in coding linters, but it seems to me that this simple includes string check could give a false positive for something like type Tree = Record<string, AnotherTree>, couldn't it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, true. Thanks for letting me know. I've fixed that in the following commit.

Comment on lines 97 to 100
const value = sourceCode.getText(valueType.typeAnnotation);
const name = node.parent?.id?.name;
const valueArray = value.split('|').map(v => v.trim());
if (valueArray.indexOf(name) != -1) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doing operations and linting based on text is not correct and will always cause false positive or false negative.

here are a number of cases you'll false-positive

type T = { [k: string]: AT };
type T = { [k: string]: TA };
type T = { [k: string]: A.T };
type T = { [k: string]: A['T'] };
type T = { [k: string]: (A: string) => void };
// ...

You need to use scope analysis here.
https://eslint.org/docs/developer-guide/scope-manager-interface

Look for references within the current node.
search this or the ESLint core codebase for context.getScope for examples

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I'll come back with a fix as soon as possible, thanks for the help.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bradzacher I did some changes but I feel the logic I implemented is too bloated, please review and let me know if I can refactor it, thankyou.

@bradzacher bradzacher added awaiting response Issues waiting for a reply from the OP or another party bug Something isn't working labels Nov 15, 2020
@codecov
Copy link

codecov bot commented Nov 16, 2020

Codecov Report

Merging #2766 (2879493) into master (2326238) will decrease coverage by 0.02%.
The diff coverage is 80.00%.

@@            Coverage Diff             @@
##           master    #2766      +/-   ##
==========================================
- Coverage   92.80%   92.77%   -0.03%     
==========================================
  Files         300      300              
  Lines        9852     9871      +19     
  Branches     2767     2778      +11     
==========================================
+ Hits         9143     9158      +15     
  Misses        332      332              
- Partials      377      381       +4     
Flag Coverage Δ
unittest 92.77% <80.00%> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
...lugin/src/rules/consistent-indexed-object-style.ts 85.93% <80.00%> (-2.96%) ⬇️

Comment on lines +114 to +127
const membersArray = memberTypes.types;
let flag = false;
membersArray.forEach((m: TSESTree.Node) => {
if (
m.type == AST_NODE_TYPES.TSTypeReference &&
m.typeName.type == AST_NODE_TYPES.Identifier &&
m.typeName.name == name
) {
flag = true;
}
});
if (flag) {
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use a for-of loop instead of .forEach and that allows you to return directly instead of using the flag:

Suggested change
const membersArray = memberTypes.types;
let flag = false;
membersArray.forEach((m: TSESTree.Node) => {
if (
m.type == AST_NODE_TYPES.TSTypeReference &&
m.typeName.type == AST_NODE_TYPES.Identifier &&
m.typeName.name == name
) {
flag = true;
}
});
if (flag) {
return;
}
const membersArray: TSESTree.Node[] = memberTypes.types;
for (const m of membersArray) {
if (
m.type == AST_NODE_TYPES.TSTypeReference &&
m.typeName.type == AST_NODE_TYPES.Identifier &&
m.typeName.name == name
) {
return;
}
}

Copy link
Member

@bradzacher bradzacher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

your code doesn't actually use the scope manager.
All you've done here is added a layer of indirection for getting the current AST node!


The scope manager is a powerful tool which lets you see the variables defined in a scope as well as the references to that variable.

const scope = context.getScope();
const variable = scope.set.get(name);
const references = variable.references;

You can look at the references to the variable and determine if the references exist outside the type declaration

@lakshyabatman
Copy link
Author

lakshyabatman commented Nov 19, 2020

@bradzacher I'm sorry for the delay, but before proceeding with any changes I thought to share the code snippet to check if it's fine from your side.

      const scope = context.getScope();
      const block = scope.block;

      if (block.type == AST_NODE_TYPES.Program) {
        if (block.body[0].type == AST_NODE_TYPES.TSTypeAliasDeclaration) {
          const name = block.body[0].id.name;
          const variables = scope.set.get(name);
          const references = variables?.references ?? [];
          if (references.length > 0) {
            if (references[0].from.type == 'global') {
              return;
            }
          }
        }
      }

Though it would work only in case of the type declaration.

Although in situations like,

interface A {
 key: A;
}

The trigger should not work.

@bradzacher bradzacher changed the title fix(eslint-plugin): fixed circular reference issue fix(eslint-plugin): [consistent-indexed-object-style] fixed circular reference issue Nov 20, 2020
@bradzacher
Copy link
Member

You don't need the block checks.

Something like this:

const scope = context.getScope();
const variable = scope.set.get(typeName);
const references = variables?.references ?? [];

for (const ref of references) {
  if (
    ref.identifier.range[0] >= interfaceOrTypeNode.range[0] &&
    ref.identifier.range[1] <= interfaceOrTypeNode.range[1]
  ) {
    // is a self-reference as the reference identifier is inside the interface/type declaration
  }
}

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Dec 21, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
awaiting response Issues waiting for a reply from the OP or another party bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[@typescript-eslint/consistent-indexed-object-style] Fails on circular references
3 participants