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
[no-shadow] Enums can false-positive ESLint's no-shadow rule #325
Comments
I'm not sure if this should be the parser (for putting an enum item into scope?) or the plugin (it should be in scope, but we need a new TS-specific |
we're working on implementing typescript-compliant scope analysis. |
Thank you for this report. However, this is intentional behavior because the const f = 777;
enum E1 {
f = 1, // Shadows the upper `f`.
g = f, // = 1
}
enum E2 {
f1 = 1,
g = f, // This is the upper `f`.
} |
In typescript, you can think of an enum declaration as the following: It's not 100%, but it makes sense if you look at an example playground const imOutside = 2;
const b = 2;
enum Foo {
outer = imOutside,
a = 1,
b = a,
c = b,
// does c == Foo.b == Foo.c == 1?
// or does c == b == 2?
} In the above example, what does This is why the example you provided is not correct. If I rewrite my example using objects: const imOutside = 2;
const b = 2;
const Foo = {
outer: imOutside,
a: 1,
b: a,
c: b,
// does c == Foo.b == Foo.c == 1?
// or does c == b == 2?
} Then I can ask the same question - what is the value of I hope this clarifies it for you. To reiterate:
|
@bradzacher sorry, tried to delete my comment before someone spent time on a response once I realized what was going on. I did eventually see that enums create a new scope. That's quite....unexpected. Thanks for taking the time, and sorry for the bother. |
@bradzacher Do you think it's reasonable to request an option for |
It's not unreasonable, no. However, for me as an engineer; I would take this as a code smell. Creating an option would create a false sense of security, and will potentially cause bugs. With that option, all it takes is one shadow usage by an engineer who doesn't know that enums have a scope, and your enum will have the wrong value. |
@bradzacher I agree that as-is, this is code smell since the enum keyword simply creates a scope for assignment. However, I have an idea of how to alleviate the issue. Given that you understand the language better than I do, I'm wondering if you have an opinion. I'm thinking of writing a separate rule in the next month or two (day job is busy) that will require the values of an enum to be primitives. I would consider basing an enum's value off of a variable instead of the other way around to be a larger bit of code smell than having the property of an object use a shadowed name. In other words: // Bad
const someString = "something";
enum Stuff {
SomeString = someString,
}
// Good
enum Stuff {
SomeString = "something",
}
const someString = Stuff.SomeString; If paired with the rule I'm wanting to write, I don't think that allowing shadowed enum keys is code smell any more. Thoughts? |
I would agree with you and that rule would be a good addition. |
I've just hit @kg-currenxie described case, indeed it's annoying and will force me to disable the rule :( @oigewan did you had a chance to work on the rule you've proposed? It seems like an adequate solution. |
OOC how often are you hitting it that it's annoying enough to disable the rule entirely? As mentioned in previous comments, to me that seems like a code smell that there are that many enum member name collisions. |
I guess I should've phrased that differently - I meant that I won't be enabling the rule in the 1st place. I wanted to introduce the rule into an existing react codebase of medium size. But after learning that I have to work around enum keys to have it play nice it pushes the rule over the limit of being more trouble than use. IMHO, thinking about my case, the smell arguments aren't very strong. I'm not planning on using any variables for enums, I'd actually like to have the additional rule proposed by @oigewan. So I can't think of a good reason why enum member names should be all that different. |
I haven't as I've switched teams and otherwise been quite busy. However, I am thinking of trying to get started this weekend. |
@Klemensas and @bradzacher I've created a pull request with the rule described in this thread: #1898 If it gets accepted, I can do a follow-up PR which will add an option to the no-shadow rule which will ignore enum members with documentation recommending that the new rule is enabled if the no-shadow option ignoring enum members is enabled. |
Sorry to chip in here. It makes sense that for variables there can be no double declaration. However for types and interfaces that should not be the case as they are not variables, this behavior seems correct if the type and enum is defined in the same file, however once they are split up in separate files this does not work anymore. E.g. The following does not throw a no-shadow error: export interface Car {
color: string;
}
export enum Vehicles {
Car = 'car',
} But when importing Car it does throw an error: export interface Car {
color: string;
} file3.ts import { Car } from './file2';
export const car: Car = {
color: 'red',
}
export enum Vehicles {
Car = 'car',
} |
Repro
Expected Result
No errors.
Actual Result
The
test
enum item gets flagged byno-shadow
.Additional Info
Seems like maybe we needs an
@typescript-eslint/no-shadow
rule to handle TypeScript's syntax for enums?Versions
@typescript-eslint/eslint-plugin
1.4.2
@typescript-eslint/parser
1.4.2
TypeScript
3.3.3333
ESLint
5.14.1
node
10.15.0
npm
6.7.0
The text was updated successfully, but these errors were encountered: