Skip to content

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

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open

feat: support TS syntax in no-redeclare #19563

wants to merge 17 commits into from

Conversation

snitin315
Copy link
Contributor

@snitin315 snitin315 commented Mar 22, 2025

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 }:

interface A {
  prop1: 1;
}
interface A {
  prop2: 2;
}

namespace Foo {
  export const a = 1;
}
namespace Foo {
  export const b = 2;
}

class Bar {}
namespace Bar {}

function Baz() {}
namespace Baz {}

Is there anything you'd like reviewers to focus on?

@github-project-automation github-project-automation bot moved this to Needs Triage in Triage Mar 22, 2025
@eslint-github-bot eslint-github-bot bot added the feature This change adds a new feature to ESLint label Mar 22, 2025
@github-actions github-actions bot added the rule Relates to ESLint's core rules label Mar 22, 2025
Copy link

netlify bot commented Mar 22, 2025

Deploy Preview for docs-eslint ready!

Name Link
🔨 Latest commit 70fa0e6
🔍 Latest deploy log https://app.netlify.com/projects/docs-eslint/deploys/686f60001ee42500083c43a6
😎 Deploy Preview https://deploy-preview-19563--docs-eslint.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@snitin315 snitin315 marked this pull request as ready for review March 26, 2025 13:19
@snitin315 snitin315 requested a review from a team as a code owner March 26, 2025 13:19
@snitin315
Copy link
Contributor Author

Tests are passing locally but failing on CI. I can reproduce the same behavior locally if I remove languageOptions.sourceType: "script option from ruleTesterTypeScript locally. I'm not sure why it's failing on CI.

@nzakas nzakas moved this from Needs Triage to Implementing in Triage Mar 27, 2025
Copy link
Contributor

@JoshuaKGoldberg JoshuaKGoldberg left a 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. 🚀

@snitin315 snitin315 added the accepted There is consensus among the team that this change meets the criteria for inclusion label Mar 31, 2025
@snitin315
Copy link
Contributor Author

@JoshuaKGoldberg This needs a re-review.

Copy link
Contributor

@JoshuaKGoldberg JoshuaKGoldberg left a 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?

JoshuaKGoldberg
JoshuaKGoldberg previously approved these changes Apr 9, 2025
Copy link
Contributor

@JoshuaKGoldberg JoshuaKGoldberg left a comment

Choose a reason for hiding this comment

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

🙌

@github-actions github-actions bot added the Stale label May 28, 2025
@nzakas
Copy link
Member

nzakas commented May 30, 2025

@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?

@snitin315
Copy link
Contributor Author

I have some changes locally, let me push an update

@github-actions github-actions bot removed the Stale label May 30, 2025
@snitin315 snitin315 moved this from Evaluating to Implementing in Triage May 31, 2025
@fasttime
Copy link
Member

fasttime commented Jun 6, 2025

Sorry for losing track of this. I will review it soon.

Copy link
Member

@fasttime fasttime left a 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.

@nzakas
Copy link
Member

nzakas commented Jun 13, 2025

If the typescript-eslint rule is not recommended for us, maybe we just shouldn't update this rule?

@fasttime
Copy link
Member

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.

@nzakas
Copy link
Member

nzakas commented Jun 18, 2025

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.

@fasttime
Copy link
Member

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?

@nzakas
Copy link
Member

nzakas commented Jun 19, 2025

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. 👍

Copy link
Member

@fasttime fasttime left a 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.

Copy link

github-actions bot commented Jul 9, 2025

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.

@github-actions github-actions bot added the Stale label Jul 9, 2025
@github-actions github-actions bot removed the Stale label Jul 10, 2025
@snitin315
Copy link
Contributor Author

@fasttime I've addressed the feedback. PTAL

Copy link
Member

@fasttime fasttime left a 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:

TypeScript Playground

Copy link

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.

@github-actions github-actions bot added the Stale label Jul 30, 2025
@fasttime
Copy link
Member

@eslint/eslint-tsc I could use some feedback on #19563 (review).

@nzakas
Copy link
Member

nzakas commented Jul 31, 2025

@github-actions github-actions bot removed the Stale label Jul 31, 2025
Copy link
Member

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

Comment on lines +1179 to +1180
function A() {}
class A {}
Copy link
Member

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.

Comment on lines +1195 to +1197
function A() {}
class A {}
namespace A {}
Copy link
Member

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.

Comment on lines +147 to +189
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;
}
Copy link
Member

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accepted There is consensus among the team that this change meets the criteria for inclusion feature This change adds a new feature to ESLint rule Relates to ESLint's core rules
Projects
Status: Implementing
Development

Successfully merging this pull request may close these issues.

5 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy