-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
feat(eslint-plugin): [strict-enums] new rule #4864
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
Conversation
Thanks for the PR, @Zamiell! typescript-eslint is a 100% community driven project, and we are incredibly grateful that you are contributing to that community. The core maintainers work on this in their personal time, so please understand that it may not be possible for them to review your work immediately. Thanks again! 🙏 Please, if you or your company is finding typescript-eslint valuable, help us sustain the project by sponsoring it transparently on https://opencollective.com/typescript-eslint. As a thank you, your profile/company logo will be added to our main README which receives thousands of unique visitors per day. |
✅ Deploy Preview for typescript-eslint ready!
To edit notification comments on pull requests, go to your Netlify site settings. |
Codecov Report
@@ Coverage Diff @@
## main #4864 +/- ##
==========================================
- Coverage 94.25% 91.53% -2.73%
==========================================
Files 153 228 +75
Lines 8305 10856 +2551
Branches 2702 3351 +649
==========================================
+ Hits 7828 9937 +2109
- Misses 263 637 +374
- Partials 214 282 +68
Flags with carried forward coverage won't be shown. Click here to find out more.
|
No idea why linting is failing in CI. It passes locally when I do |
I also found a bug with the Which means that the function signature of this method needs to be updated in the TypeScript repository. |
also fixes for codebase
Some notes:
Needs to still be addressed:
The documentation here recommends using Here's a list of errors that I ignored:
This file has code that uses a reducer on an enum array, but with a starting value of Thus, the
This uses a complex type of
My first thought was to change this to The comment for the /**
* This represents a string whose leading underscore have been escaped by adding extra leading underscores.
* The shape of this brand is rather unique compared to others we've used.
* Instead of just an intersection of a string and an object, it is that union-ed
* with an intersection of void and an object. This makes it wholly incompatible
* with a normal string (which is good, it cannot be misused on assignment or on usage),
* while still being comparable with a normal string via === (also good) and castable from a string.
*/
export type __String = (string & {
__escapedIdentifier: void;
}) | (void & {
__escapedIdentifier: void;
}) | InternalSymbolName; So I guess it's just weird and I should just put
Same as "no-implied-eval.ts".
Same as "no-implied-eval.ts".
Same as "no-implied-eval.ts". This monorepo is a good test case for finding bugs in the rule. For example, it caught a bug with variadic functions, which I didn't have a test case for originally. Still working on some of the other errors. |
Co-authored-by: Joshua Chen <sidachen2003@gmail.com>
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.
This is looking really good to me as it currently stands.
HOWEVER, I do see a large DevX problem in terms of "flags" enums. Now, flags enums are known to be unsafe.
Personally, I think in JS you should be using a Set
or array to store the a list of possible values, but I get why one might use a "flags" enum.:
- they're often used is for memory efficiency.
- for example - this is why TS went with a "flags" enum for its fields - saves a lot of memory across an entire project's ASTs.
- some would argue that there's a nice DevX to using bitwise-ops on flag enums.
- personally I think that they're a bit confusing to think about, though I can understand how they're familiar for someone comfortable with lower-level languages.
The point is though, this may be something we need to solve for in terms of devx.
Right now the answer is "too bad, so sad - union in number
", but I don't think this is a good answer for those users because it destroys the type documentation for the usecases, for example:
Whilst I understand that in these cases it is "technically correct" for the type to be number
, it's also not truly correct as it suggests things like "it's valid to pass 100 to this function", when it's not - because 100 satisfies neither Foo.A
, nor Foo.B
.
So with all that said, I think we need to determine a means for people to "opt out" of the rule for flags enums.
Perhaps it's something as simple as adding an option with an "allow flags naming pattern" similar to the no-unused-vars
rule's varsIgnorePattern
option:
{
"@typescript-eslint/strict-enums": [
"error",
// treats any enum with name ending in "Flags" as a flags enum
{"flagsEnumPattern": "Flags^"},
],
}
This way users can have the rule on for strict usecase, and opt-out (with a clear naming pattern) known "unsafe" usecases.
I know we'd certainly find value within this very codebase thinks to the TS flag enums!
WDYT?
@@ -65,7 +65,7 @@ class Reference { | |||
/** | |||
* In some cases, a reference may be a type, value or both a type and value reference. | |||
*/ | |||
readonly #referenceType: ReferenceTypeFlag; | |||
readonly #referenceType: number | ReferenceTypeFlag; |
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.
instead of unioning in number here, could we define TypeOrNumber
as an enum member, and remove the bitwise operations on the enum?
*/ | ||
export function getTypeFlags(type: ts.Type): ts.TypeFlags { | ||
let flags: ts.TypeFlags = 0; | ||
export function getTypeFlags(type: ts.Type): number | ts.TypeFlags { |
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.
@JoshuaKGoldberg the point @Zamiell was making was that the code is written like this:
getTypeFlags(flag.one | flag.two | ...);
when could be written like this:
getTypeFlags(new Set([flag.one, flag.two, ...]);
VariableDeclaration(node): void { | ||
for (const declaration of node.declarations) { |
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.
nitpick - we don't care about the declaration itself, just the declarators.
VariableDeclaration(node): void { | |
for (const declaration of node.declarations) { | |
VariableDeclarator(node): void { |
This is good because it saves you manually iterating and it means your report will be cleaner.
I.e. instead of:
const x = 1,
^^^^^^^^^^^^
y: Foo = 99;
^^^^^^^^^^^^^^^^^
(where ^
is the report underline)
you'll instead have:
const x = 1,
y: Foo = 99;
^^^^^^^^^^^
Which is clearer and more accurate!
Co-authored-by: Brad Zacher <brad.zacher@gmail.com>
Co-authored-by: Brad Zacher <brad.zacher@gmail.com>
Co-authored-by: Brad Zacher <brad.zacher@gmail.com>
There are branch conflicts in thre README.md files.
|
Also, after Brad's updates, I get the following errors when building:
Anyone know why that is? I'm unable to work on the branch until that is resolved. |
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 think this is looking good so far.
I'm happy if we want to get this in and out to people to play with.
Fix the merge conflicts and we can get to it!
## Banned Patterns | ||
|
||
This rule bans: | ||
|
||
1. Enum incrementing/decrementing - `incorrectIncrement` | ||
1. Mismatched enum declarations/assignments - `mismatchedAssignment` | ||
1. Mismatched enum comparisons - `mismatchedComparison` | ||
1. Mismatched enum function arguments - `mismatchedFunctionArgument` |
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.
this is just a less-verbose duplicate of "Error Information" below - so let's remove this.
Brad can you respond to my messages on May 31st? |
@Zamiell - both At the time the "canonical" table was the former (root readme), and the latter was added as part of the website build. However the rules table is now no longer generated in source files - instead they are generated as part of the website build. |
👋 @Zamiell, just checking in - do you still have time to work on this? And if so, are there still open questions you're waiting on answers to? |
Closing this PR as it's been stale for a few months without activity. Feel free to reopen @Zamiell if you have time - but no worries if not! If anybody wants to drive it forward, please do post your own PR - and if you use this as a start, consider adding |
Moving to #6091. I want these checks to exist 😄 |
PR Checklist
Overview
Makes working with enums not suck. See the docs that I wrote for the rule.
This PR is a draft; I am still working on it but I will start a PR for now in case anyone wants to give me early feedback.