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

Accept concrete (unused) implementations of public abstract methods #360

Closed
Jym77 opened this issue Nov 22, 2023 · 9 comments
Closed

Accept concrete (unused) implementations of public abstract methods #360

Jym77 opened this issue Nov 22, 2023 · 9 comments
Labels
feature request Feature request

Comments

@Jym77
Copy link

Jym77 commented Nov 22, 2023

I think knip should accept the concrete implementation of an abstract method, even when the concrete version is not used directly, when the abstract method is either @public, or used.

foo.ts:

export abstract class AFoo {
  /** @public */
  public abstract foo(): number
}

export class Foo {
  public foo() {
    return 0
  }
}

index.ts:

import { AFoo, Foo } from "./foo";

const x = new Foo()

function f(x: AFoo): number {
  return x.foo()
}

When knipping that, I got:

Unused exported class members (1)
foo  Foo  src/foo.ts

But Foo#foo is pretty much needed, and even if function f wasn't here, it would be required to implement the abstract method which I've already told knip is meant for external API.
Adding a release tag on Foo#foo is not really convenient since I have around 40 of these subclasses in my actual case 🙈

@Jym77 Jym77 added the feature request Feature request label Nov 22, 2023
@webpro
Copy link
Collaborator

webpro commented Nov 28, 2023

How about this:

export abstract class AFoo {
  public abstract foo(): number
}

export class Foo implements AFoo {
  public foo() {
    return 0
  }
}
import { Foo } from "./foo";

const x = new Foo()

function f(x: Foo): number {
  return x.foo()
}

@Jym77
Copy link
Author

Jym77 commented Nov 28, 2023

🙈
Yep, forgot that in the MNWE…

It indeed seems to work with both extends and implements.
In my real code with 3 levels of abstract classes before the concrete one, it broke 🤔

@Jym77
Copy link
Author

Jym77 commented Nov 28, 2023

🤔 I'm still getting it in my large real code, but can't make a MNWE about it. That might be another root cause that my initial analysis…

@webpro
Copy link
Collaborator

webpro commented Nov 28, 2023

Not sure what I can do here. Feel free to close this issue or provide a MNWE.

@Jym77
Copy link
Author

Jym77 commented Nov 28, 2023

Managed to get a MNWE: https://codesandbox.io/p/devbox/mystifying-swanson-ytr3wq

The classes After (in its own file) ad Before (in the same file as the parent abstract class) are exactly the same, but After is (incorrectly, as it needs to implement the public abstract from Selector) reported for unused [Symbol.iterator] while Before isn't.

@webpro
Copy link
Collaborator

webpro commented Dec 1, 2023

I still don't see actual usage of the member reported as unused? I see classes, no instances.

@Jym77
Copy link
Author

Jym77 commented Dec 4, 2023

Played a bit more with the MNWE. It seems to be rather linked to [Symbol.iterator] treatment 🤔

  • The abstract class Selector has a foo and [Symbol.iterator] abstract methods. It is exported from the index.ts which more or less marks it as external API with no need for internal usage (as far as I understand).
  • Both the After and Before concrete classes are mostly the same (except the return type of bar and the fact that Before is not exported from its file). But live in different files.
  • knip (correctly) reports that After#bar is unused. It doesn't report After#foo, I'm not fully sure why since neither it nor Selector#foo is marked as @public for external API. I assume that Selector being exported from top-level is enough to mark its method as "for external use, don't report" 🤔 (this hypothesis is confirmed by uncommenting the abstract bar in Selector, which stops knip from reporting After#bar).
  • However, knip reports After#[Symbol.iterator] I fail to see the difference between this method and After#foo (except for the fact that the name is a symbol, not an identifier) 💥 (actually, Selector#[Symbol.iterator] is actually used in the function in index.ts, while Selector#foo is not used anywhere, so I'd expect After#foo to be reported rather than After#[Symbol.iterator]).
  • I'm also not fully sure why nothing is reported from Before. Not even the fact that Before itself is never used anywhere 💥

(PS: thank for the tool and the responsiveness to questions 😄, don't take it wrong I love it)

@webpro
Copy link
Collaborator

webpro commented Dec 8, 2023

For now:

  • Unused exports in entry files are not reported (you can enable this with --include-entry-exports)
  • Members of exports in entry files are also not reported. However, --include-entry-exports does not enable this for members, so I think this is a bug I'm going to fix. You saw that right, thanks!
  • Before is not exported, so it's not reported. This is ESLint (or other isolated file linter) area :)

@webpro
Copy link
Collaborator

webpro commented Mar 24, 2024

🚀 This issue has been resolved in v5.3.1. See Release 5.3.1 for release notes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request Feature request
Projects
None yet
Development

No branches or pull requests

2 participants