Skip to content
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

Bug: [no-unused-vars] false positive when using decorators on class #6260

Closed
4 tasks done
weareoutman opened this issue Dec 21, 2022 · 13 comments
Closed
4 tasks done
Labels
enhancement: plugin rule option New rule option for an existing eslint-plugin rule package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin wontfix This will not be worked on working as intended Issues that are closed as they are working as intended

Comments

@weareoutman
Copy link

Before You File a Bug Report Please Confirm You Have Done The Following...

  • I have tried restarting my IDE and the issue persists.
  • I have updated to the latest version of the packages.
  • I have searched for related issues and found none that matched my issue.
  • I have read the FAQ and my problem is not listed.

Playground Link

https://typescript-eslint.io/play/#ts=4.9.3&sourceType=module&code=GYVwdgxgLglg9mABAEwKYQBQEoBciCGYAnogN4BQAvueQAJqZbkQA2+Azu4gEJk2VA&eslintrc=N4KABGBEBOCuA2BTAzpAXGUEKQHYHsBaWXWZRAE0IDcBDaVDSfAMxcgBpxtIABAFwCeABxQBjaAEth-QiniTc-APQFipclToN0UAO71ckbgF8QJoA&tsconfig=N4KABGBEDGD2C2AHAlgGwKYCcDyiAuysAdgM6QBcYoEEk6AHolsvOkXgIaoAi6cmHPLExlKeTAFd04MAF8QsoA

Repro Code

function dec(): any {
}

@dec()
class B {

}

ESLint Config

module.exports = {
  "rules": {
    "no-unused-vars": "off",
    "@typescript-eslint/no-unused-vars": "warn"
  }
}

tsconfig

{
  "compilerOptions": {
    "experimentalDecorators": true
  }
}

Expected Result

No warnings.

Actual Result

'B' is defined but never used.

Additional Info

No response

@weareoutman weareoutman added bug Something isn't working package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin triage Waiting for team members to take a look labels Dec 21, 2022
@JoshuaKGoldberg
Copy link
Member

🤔 I would think most users would consider this to be an unused class. Anecdotally speaking, most class decorators I've seen in use today only modify the class - they don't send it somewhere else.

Perhaps this needs to be an opt-in option using the format in discussion in #6017? Marking as blocked pending that discussion's resolution (waiting on eslint/eslint#16540).

@JoshuaKGoldberg JoshuaKGoldberg added enhancement: plugin rule option New rule option for an existing eslint-plugin rule blocked by another issue Issues which are not ready because another issue needs to be resolved first and removed bug Something isn't working triage Waiting for team members to take a look labels Dec 21, 2022
@weareoutman
Copy link
Author

Actually my case is using decorators to define custom elements, just like the introduction example of the tc39/proposal-decorators

@defineElement("my-class")
class C extends HTMLElement {
  @reactive accessor clicked = false;
}

@weareoutman
Copy link
Author

weareoutman commented Dec 21, 2022

I agree that we can't tell a decorator whether would use the decorated target, just by AST.

I didn't fully understand your proposal in that discussion, but I think we can provide an opt-in option to mark some decorators as having side effects on the decorated target?

@JoshuaKGoldberg
Copy link
Member

an opt-in option to mark some decorators as having side effects!

Yup! The tricky part is specifying the format for that opt-in option. Enough rules have this need that we're trying to make one unified format. That discussion is talking about how we want that format to look.

@weareoutman
Copy link
Author

👍 Looking forward to it

@bradzacher
Copy link
Member

bradzacher commented Dec 21, 2022

The rule is correct here based purely on semantics.

Why? Because decorators are just syntactic sugar that does something like this:

@deco
class Foo {}

// same as

const Foo = deco(class Foo {})

Notice how regardless of what side-effects the decorator may or may not do, there is still an unused variable left defined?

To put this another way, would you consider this to be an unused variable:

function sideEffect(arg) {
  window.store.add(arg);
  return arg;
}

const foo = sideEffect(1);

I'd hope you'd answer yes, as the variable is clearly unused.

Given that the rule is correct here, the question is whether or not there should be an option to ignore this sort of case?
I'd personally be leaning towards no for two reasons:

  1. this is the first time this has been asked for ever, so it's not a common want for the community (I.e. This currently is a niche request).
  2. unless I'm mistaken, you could rewrite your code to remove the unused variable by not using a decorator (defineElement('my-class') (class C {})).

(1) may change over time for sure as decorators are standardised fully and more use cases are explored by the community. If it does I think we could definitely revisit this discussion (though at that point it would be in the context of the core rule).

@weareoutman
Copy link
Author

@bradzacher Thanks for your patient explaining, but I guess I'll keep using it as decorator and ignore such warnings, : (

@weareoutman
Copy link
Author

weareoutman commented Dec 22, 2022

As you mentioned above of the de-sugared code is like:

const Foo = deco(class Foo {})

Even though only the latter Foo is used, but since they both share the name of Foo, we can't just say that Foo (in source code, there is only one Foo) is not used. I think the only unused thing is the class name, while the class itself (or say the class body) has been used. It's a bit like class expression with or without a name.

Currently in vscode, if I click "Quick Fix", the editor will remove my whole code, which is of course wrong I think (but it maybe the problem of typescript)

@bradzacher
Copy link
Member

@weareoutman no-unused-vars does not include a fixer, so any quick fix you're using is provided by another tool.

@weareoutman
Copy link
Author

@bradzacher Yes, it's provided by TypeScript, I also fired an issue there, since TS reports an unused error for the same use case, too. It seems that someone else has the same opinion as you mentioned above, however, I don't agree with that. I explained with more of my thoughts on that issue.

aarongable pushed a commit to chromium/chromium that referenced this issue Jan 18, 2023
This is done because the use of TypeScript annotations causes this check
to produce false positives with TypeScript version 4.9, which seems to
be a known issue, see [1] and [2].

[1] typescript-eslint/typescript-eslint#6260
[2] microsoft/TypeScript#51992

Bug: b/265863256
Change-Id: Idc9079a0a4fe9b88b10f6abb065f95bbd53f1a57
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4176095
Reviewed-by: Joel Hockey <joelhockey@chromium.org>
Auto-Submit: Demetrios Papadopoulos <dpapad@chromium.org>
Commit-Queue: Joel Hockey <joelhockey@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1093689}
@oceanofmaya
Copy link

This affects a lot of projects I work on where every entity I create for use with TypeORM has decorators. Refer https://github.com/typeorm/typeorm/blob/master/docs/entities.md

@bradzacher
Copy link
Member

bradzacher commented Mar 10, 2023

We do not encode semantics for framework-specific side-effects in our lint rules.
We purely work on the code you provide.
If the class is not referenced, but is indirectly used by a side-effect in the generator - then it's unused.

If you'd like to encode information about your specific framework's side-effects into the rule - you can freely do that by creating a new rule that marks the variable as used using ESLint's standard API, context.markVariableAsUsed:
https://eslint.org/docs/latest/extend/custom-rules#:~:text=markVariableAsUsed(name)%20%2D%20marks%20a%20variable%20with%20the%20given%20name%20in%20the%20current%20scope%20as%20used.%20This%20affects%20the%20no%2Dunused%2Dvars%20rule.%20Returns%20true%20if%20a%20variable%20with%20the%20given%20name%20was%20found%20and%20marked%20as%20used%2C%20otherwise%20false.

Here are two example rules that do exactly that:

@bradzacher bradzacher closed this as not planned Won't fix, can't repro, duplicate, stale Mar 10, 2023
@bradzacher bradzacher added working as intended Issues that are closed as they are working as intended wontfix This will not be worked on and removed blocked by another issue Issues which are not ready because another issue needs to be resolved first labels Mar 10, 2023
@weareoutman
Copy link
Author

Thanks for your advice of custom rules, that seems to be the right way

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 18, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement: plugin rule option New rule option for an existing eslint-plugin rule package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin wontfix This will not be worked on working as intended Issues that are closed as they are working as intended
Projects
None yet
Development

No branches or pull requests

4 participants