webpro-nl / knip

✂️ Find unused files, dependencies and exports in your JavaScript and TypeScript projects. Knip it before you ship it!
https://knip.dev
ISC License
6.67k stars 158 forks source link

Object property enumeration methods used on enum reporting as unused exported enum members #699

Closed shawnmcknight closed 6 hours ago

shawnmcknight commented 3 months ago

I've created a reproduction at https://github.com/shawnmcknight/knip-enum-object-values.

In this scenario, there is an enum with initializers:

export enum Fruits {
  apple = "apple",
  orange = "orange",
}

That enum is imported and run through any of the Object property enumerators:

const fruitKeys = Object.keys(Fruits);
const fruitValues = Object.values(Fruits);
const fruitEntries = Object.entries(Fruits);

With this code, each enum member is reported as unused by knip, but effectively every enumerated value is being used. I'm not sure I would consider Object.keys to be "using" the enum member, but both Object.values and Object.entries are consuming the entirety of the enumeration.

Please let me know if there is anything else I can provide to assist!

webpro commented 3 months ago

Knip doesn't really support any kind of dynamics like the one reported. It's basically mostly just direct references or "dotted" references (for e.g. namespaces, enum members).

However, perhaps we could such handle situations a bit like https://knip.dev/guides/namespace-imports and when an enum (or namespace) is used as an argument in eg. Object.keys and perhaps other enumerations/iterations we consider them all "referenced".

shawnmcknight commented 3 months ago

This situation does sound a lot like the namespace example. As noted there, you don't really know if the namespace is being used by send, but more than likely you want to consider it to be completely referenced. This situation is pretty much the same -- its unknown whether each enum member is actually being used, but you probably don't want it to be reported.

To elaborate a bit on where this presented itself, we have scenarios where the enum members needed to be turned into an array of strings for validation purposes (e.g. mongoose schema enum constraints). The enum members might not need to be referenced directly since they might simply flow in and out as the initialized strings. The enum makes a nice container for these values and when necessary, individual enum members could be referenced, even though they all don't have to be.

muuvmuuv commented 2 months ago

I have the exact same situation and one more where it is used as a type only:

export enum Gender {
    MALE = 'male',
    FEMALE = 'female',
    DIVERS = 'divers',
    ANY = 'any',
}

export const genders = Object.values(Gender)

export interface Profile {
    gender?: Gender
}
webpro commented 3 hours ago

You can this before the release:

npm i -D https://pkg.pr.new/knip@e4bada4

Note that the behavior is not the same as with the namespace imports: enum usage in an Object enumeration method means no members will be reported. Regardless of whether also one or more members are referenced somewhere. Happy to discuss if this is surprising behavior.