-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
feat: support TS syntax in no-redeclare
#19563
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
base: main
Are you sure you want to change the base?
Conversation
✅ Deploy Preview for docs-eslint ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
f38129c
to
42b3fd8
Compare
Tests are passing locally but failing on CI. I can reproduce the same behavior locally if I remove |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think I have enough context to weigh in on the sourceType
points, but otherwise this looks very reasonable! Just one area of code I think can be deduplicated. 🚀
@JoshuaKGoldberg This needs a re-review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to admit my confidence in declaration merging logic has never been super high - but I think there are some points of simplification we could go for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🙌
@snitin315 please double-check the lint failure. @JoshuaKGoldberg @mdjermanovic @fasttime this PR has been auto-closed for inactivity multiple times. What do you want to do here? |
I have some changes locally, let me push an update |
Sorry for losing track of this. I will review it soon. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The typescript-eslint rule no-redeclare
, upon which this PR is based, is not recommended for new projects, because it reports problems that are already reported by the TypeScript compiler, see https://typescript-eslint.io/rules/no-redeclare/. The built-in no-redeclare
rule on the other hand is recommended, and it reports some problems that are not reported by tsc, like var
redeclarations.
I'm not sure if we want to add the unrecommended TypeScript extensions to this rule without an option.
If the typescript-eslint rule is not recommended for us, maybe we just shouldn't update this rule? |
I'm not sure. It seems that all new problems the rule would find will be also reported by TypeScript, provided that the file is tested. |
Right. If updating this rule just adds noise that is already caught by TypeScript, I'm not sure it's worth it. |
Then maybe we should update just the documentation and rule metadata, and keep only the new tests that ensure the rule works as intended with TypeScript files? |
Makes sense to me. 👍 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've commented on some invalid cases which are actually considered valid TypeScript code and are also treated as valid by the typescript-eslint rule. Initially, I didn't realize our rule would report those usages as invalid without modification. To better align with user expectations, I think it would be good to ignore TS type-only declarations (the ones that are stripped out from code during compilation) like type
, interface
, namespace
, declare function
, etc. These are already handled by TypeScript and are safe to redeclare if the declarations are compatible.
Hi everyone, it looks like we lost track of this pull request. Please review and see what the next steps are. This pull request will auto-close in 7 days without an update. |
@fasttime I've addressed the feedback. PTAL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the update. There's a particular scenario I'm not sure about. What should happen when a value and a type share the same name? For example:
const a = 42; // value
type a = string; // type
or
let b: object; // value
interface b { foo: string } // type
or also
/* global c */
type c = number;
The TypeScript compiler considers these valid code because there is no collision between values and types even if they share the same name:
Hi everyone, it looks like we lost track of this pull request. Please review and see what the next steps are. This pull request will auto-close in 7 days without an update. |
@eslint/eslint-tsc I could use some feedback on #19563 (review). |
I think we should follow the typescript-eslint behavior, which is to flag this as a redeclaration: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These cases should be valid:
declare function a(): void;
declare function a(): void;
declare function b(): void;
declare class b { }
enum c { }
namespace c { }
declare enum d { }
interface e { }
typescript-eslint doesn't report any violations: typescript-eslint Playground
function A() {} | ||
class A {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be no need to report this case since it's a syntax error in JavaScript and it doesn't compile in TypeScript: typescript-eslint Playground.
function A() {} | ||
class A {} | ||
namespace A {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above, this is incorrect code, so the first error seems superfluous.
if ( | ||
interfaces > 1 && | ||
classes === 0 && | ||
namespaces === 0 && | ||
functions === 0 | ||
) { | ||
return true; | ||
} | ||
|
||
// Multiple namespaces only | ||
if ( | ||
namespaces > 1 && | ||
interfaces === 0 && | ||
classes === 0 && | ||
functions === 0 | ||
) { | ||
return true; | ||
} | ||
|
||
// Interface + class (+ optional namespace) | ||
if (interfaces === 1 && classes === 1 && functions === 0) { | ||
return true; | ||
} | ||
|
||
// Class + namespace (no interface or function) | ||
if ( | ||
classes === 1 && | ||
namespaces >= 1 && | ||
interfaces === 0 && | ||
functions === 0 | ||
) { | ||
return true; | ||
} | ||
|
||
// Function + namespace (no interface or class) | ||
if ( | ||
functions === 1 && | ||
namespaces >= 1 && | ||
interfaces === 0 && | ||
classes === 0 | ||
) { | ||
return true; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this logic, the rule is making explicit exceptions for certain combinations of declaration types that should not be reported.
I think it would be easier to do the opposite: start by allowing all combinations, and report an error only for specific cases, for example when counts.others > 0
, counts.functions > 1
, and probably not much more.
Prerequisites checklist
What is the purpose of this pull request? (put an "X" next to an item)
[ ] Documentation update
[ ] Bug fix (template)
[ ] New rule (template)
[ ] Changes an existing rule (template)
[ ] Add autofix to a rule
[ ] Add a CLI option
[ ] Add something to the core
[ ] Other, please explain:
What changes did you make? (Give an overview)
Refs #19173
Adds TS support to
no-redeclare
. Added a new typescript only option similar to typescript-eslint/no-redeclare:Examples of correct code with
{ ignoreDeclarationMerge: true }
:Is there anything you'd like reviewers to focus on?