Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat(eslint-plugin): [no-unsafe-enum-comparison] add rule#6107

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
101 commits
Select commitHold shift + click to select a range
481fb44
feat: adding enums to member-ordering rule
ZamiellApr 25, 2022
5776a01
Merge branch 'main' into strict-enums
ZamiellApr 28, 2022
094c90e
feat(eslint-plugin): [strict-enums] adding check for null/undefined
ZamiellApr 28, 2022
311b1f8
feat(eslint-plugin): [strict-enums] refactoring tests to separate files
ZamiellApr 28, 2022
ac5e6d9
feat(eslint-plugin): [strict-enums] allowing more operators
ZamiellApr 28, 2022
7d55248
fix(eslint-plugin): [strict-enums] adding union test
ZamiellApr 29, 2022
74af3d1
fix(eslint-plugin): [strict-enums] refactoring + adding failing class
ZamiellApr 29, 2022
1eb70ee
fix(eslint-plugin): [strict-enums] adding constraint code
ZamiellApr 29, 2022
cf006fd
fix(eslint-plugin): [strict-enums] better eslint messages
ZamiellApr 29, 2022
4d7f3fe
Merge branch 'main' into strict-enums
ZamiellApr 29, 2022
2bbc2b7
fix(eslint-plugin): [strict-enums] removing vscode setting changes
ZamiellApr 29, 2022
75c2252
Merge branch 'main' into strict-enums
ZamiellApr 29, 2022
3976fdc
fix: changing function definition to reflect reality
ZamiellApr 29, 2022
2406d03
fix: pass string enum literal into function that take string
ZamiellApr 29, 2022
4244376
fix: allow passing enums to functions with any/unknown
ZamiellApr 29, 2022
8b4dcdc
fix: using flags instead of names
ZamiellApr 29, 2022
12f8120
fix: adding test that breaks the rule
ZamiellApr 29, 2022
e638718
fix: adding test for variadic functions
ZamiellApr 29, 2022
aef971a
fix: adding isSymbolFlagSet internally
ZamiellApr 29, 2022
75f60b9
fix: adding ignoring for remaining lint failures
ZamiellApr 30, 2022
d784aa0
fix: better comments
ZamiellApr 30, 2022
811de98
fix: broken test
ZamiellApr 30, 2022
9a71d45
fix: adding failing test for generic functions
ZamiellApr 30, 2022
f93a02a
fix: refactoring tests + adding tests
ZamiellApr 30, 2022
2ced9f8
fix: refactoring enum helper function locations
ZamiellApr 30, 2022
0695c1b
fix: cleanup
ZamiellApr 30, 2022
7272a95
fix: refactoring + fixing tests
ZamiellMay 1, 2022
6ed1f66
fix: more tests
ZamiellMay 1, 2022
83c70e3
fix: refactoring and making tests pass
ZamiellMay 1, 2022
0ff3096
fix: adding array code, all tests pass now
ZamiellMay 2, 2022
ddebded
fix: adding failing test
ZamiellMay 2, 2022
c8b239b
fix: allow empty arrays
ZamiellMay 2, 2022
90981a9
fix: adding logic for arrays with no enums
ZamiellMay 2, 2022
32dec63
fix: adding more tests
ZamiellMay 2, 2022
d0dbcbb
fix: fixing test
ZamiellMay 2, 2022
c63c518
fix: fixing linter
ZamiellMay 2, 2022
c5cf7f3
Merge branch 'main' into strict-enums
ZamiellMay 2, 2022
1b26d74
Merge branch 'main' into strict-enums
ZamiellMay 2, 2022
6e4f705
fix: reverting comment fixes
ZamiellMay 3, 2022
a0a2ee6
fix: removing refactor
ZamiellMay 3, 2022
dbae5db
fix: removing fixes to dot-notation
ZamiellMay 3, 2022
16965d9
fix: removing semi refactor
ZamiellMay 3, 2022
8b8f8f8
fix: removing jest logic
ZamiellMay 3, 2022
5f437cc
fix: removing comparison operators check
ZamiellMay 3, 2022
b1dbeb6
fix: adding failing test
ZamiellMay 3, 2022
25caa05
fix: making test pass
ZamiellMay 3, 2022
f42de61
fix: adding comment
ZamiellMay 3, 2022
a866ddd
fix: adding back in bitwise operator checks since apparently they are
ZamiellMay 3, 2022
61cf107
fix: remove bad comment
ZamiellMay 3, 2022
304d796
fix: removing unnecessary comments
ZamiellMay 3, 2022
83c5f30
fix: remove types from error messages
ZamiellMay 3, 2022
23ac918
fix: removing isArray + refactoring
ZamiellMay 4, 2022
098d732
Merge branch 'main' into strict-enums
ZamiellMay 4, 2022
99424e1
Update packages/eslint-plugin/src/rules/strict-enums.ts
ZamiellMay 4, 2022
0de3b26
fix: removing strict-enums from recommended
ZamiellMay 4, 2022
2d488cc
fix: simplify
ZamiellMay 5, 2022
15dc3df
fix: undoing refactoring
ZamiellMay 6, 2022
a3d0122
fix: undoing refactoring
ZamiellMay 6, 2022
5cd8fa7
fix: moving tests into subdirectory
ZamiellMay 6, 2022
a236085
Update packages/eslint-plugin/src/rules/strict-enums.ts
ZamiellMay 6, 2022
c357432
Update packages/eslint-plugin/src/rules/strict-enums.ts
ZamiellMay 6, 2022
2395998
fix: adding failing test
ZamiellMay 6, 2022
2851c3b
fix: making boolean tests pass
ZamiellMay 6, 2022
149c049
Merge branch 'main' into strict-enums
ZamiellMay 6, 2022
cf7c9b7
fix: refactor tests + fix linter
ZamiellMay 6, 2022
3b2fa57
fix: adding brads tests
ZamiellMay 6, 2022
6c43f71
fix: brads tests now pass
ZamiellMay 6, 2022
af7d5da
Update packages/eslint-plugin/docs/rules/strict-enums.md
ZamiellMay 7, 2022
a50f6e1
Merge branch 'main' into strict-enums
ZamiellMay 10, 2022
85563be
Update packages/eslint-plugin/src/rules/strict-enums.ts
ZamiellMay 30, 2022
ee96ce5
Update packages/eslint-plugin/src/rules/strict-enums.ts
ZamiellMay 30, 2022
943bf9f
Update packages/eslint-plugin/src/rules/strict-enums.ts
ZamiellMay 30, 2022
7679c4a
fix: make brads updates actually compile
ZamiellJun 1, 2022
871e9ca
Update strict-enums.ts
ZamiellJun 1, 2022
f861606
Merge branch 'main'
JoshuaKGoldbergNov 25, 2022
cbe9e90
Continued fixing merge conflicts
JoshuaKGoldbergNov 25, 2022
c37165c
Continued fixing the build
JoshuaKGoldbergNov 25, 2022
3f37c67
Passing build
JoshuaKGoldbergNov 25, 2022
c942261
Update packages/eslint-plugin/src/rules/strict-enums.ts
JoshuaKGoldbergNov 25, 2022
b2947f9
Update packages/eslint-plugin/src/rules/strict-enums.ts
JoshuaKGoldbergNov 25, 2022
a7772d2
A few more reverts
JoshuaKGoldbergNov 25, 2022
4204a60
Just a bit more changing typeFlagUtils
JoshuaKGoldbergNov 25, 2022
89bcd73
Fixed strict-enums.md build
JoshuaKGoldbergNov 25, 2022
4f13db4
Convert tests to not pushing
JoshuaKGoldbergNov 25, 2022
4706e4f
Simplified the rule a whole bunch
JoshuaKGoldbergNov 25, 2022
be144e3
Add back getEnumNames
JoshuaKGoldbergNov 25, 2022
e4ac325
Even more trimming down
JoshuaKGoldbergNov 25, 2022
62ccdb1
...and just a bit more
JoshuaKGoldbergNov 25, 2022
da7501a
Undo some JSDoc changes
JoshuaKGoldbergNov 25, 2022
83c33cb
Progress: no-unsafe-enum-assignment is tested
JoshuaKGoldbergNov 26, 2022
a3e6236
A bit more testing for assignments, as requested
JoshuaKGoldbergNov 27, 2022
f1071fa
Finished testing and sharing
JoshuaKGoldbergNov 27, 2022
680293a
Added comparison operators
JoshuaKGoldbergNov 27, 2022
51bc2b1
Merge branch 'main'
JoshuaKGoldbergNov 27, 2022
14cceea
Added back no-unsafe-enum-comparison
JoshuaKGoldbergNov 27, 2022
bc65e3c
Remove unrelated changes
JoshuaKGoldbergNov 27, 2022
adf6f2e
Merge branch 'main' into no-unsafe-enum-comparison
JoshuaKGoldbergDec 11, 2022
68ad65c
Reduce coverage
JoshuaKGoldbergDec 11, 2022
57376a7
Merge branch 'main'
JoshuaKGoldbergApr 5, 2023
559c0ab
Touched up docs
JoshuaKGoldbergApr 5, 2023
b1c9bec
Merge branch 'main' into no-unsafe-enum-comparison
JoshuaKGoldbergApr 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
---
description: 'Disallow comparing an enum value with a non-enum value.'
---

> 🛑 This file is source code, not the primary documentation location! 🛑
>
> See **https://typescript-eslint.io/rules/no-unsafe-enum-comparison** for documentation.

The TypeScript compiler can be surprisingly lenient when working with enums.
For example, it will allow you to compare enum values against numbers even though they might not have any type overlap:

```ts
enum Fruit {
Apple,
Banana,
}

declare let fruit: Fruit;

fruit === 999; // No error
```

This rule flags when an enum typed value is compared to a non-enum `number`.

<!--tabs-->

### ❌ Incorrect

```ts
enum Fruit {
Apple,
}

declare let fruit: Fruit;

fruit === 999;
```

```ts
enum Vegetable {
Asparagus = 'asparagus',
}

declare let vegetable: Vegetable;

vegetable === 'asparagus';
```

### ✅ Correct

```ts
enum Fruit {
Apple,
}

declare let fruit: Fruit;

fruit === Fruit.Banana;
```

```ts
enum Vegetable {
Asparagus = 'asparagus',
}

declare let vegetable: Vegetable;

vegetable === Vegetable.Asparagus;
```

<!--/tabs-->

## When Not to Use It

If you don't mind number and/or literal string constants being compared against enums, you likely don't need this rule.
1 change: 1 addition & 0 deletionspackages/eslint-plugin/src/configs/all.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -116,6 +116,7 @@ export = {
'@typescript-eslint/no-unsafe-assignment': 'error',
'@typescript-eslint/no-unsafe-call': 'error',
'@typescript-eslint/no-unsafe-declaration-merging': 'error',
'@typescript-eslint/no-unsafe-enum-comparison': 'error',
'@typescript-eslint/no-unsafe-member-access': 'error',
'@typescript-eslint/no-unsafe-return': 'error',
'no-unused-expressions': 'off',
Expand Down
1 change: 1 addition & 0 deletionspackages/eslint-plugin/src/configs/strict.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,6 +29,7 @@ export = {
'@typescript-eslint/no-unnecessary-condition': 'warn',
'@typescript-eslint/no-unnecessary-type-arguments': 'warn',
'@typescript-eslint/no-unsafe-declaration-merging': 'warn',
'@typescript-eslint/no-unsafe-enum-comparison': 'warn',
Copy link
Member

Choose a reason for hiding this comment

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

changing the recommended configs is considered a breaking change

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Oh shoot

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Ah wait, perhttps://typescript-eslint.io/linting/configs,strict isn't considered stable. It's not in our "recommended" configs list. 😌

Still filing#6890 just in case.

'no-useless-constructor': 'off',
'@typescript-eslint/no-useless-constructor': 'warn',
'@typescript-eslint/non-nullable-type-assertion-style': 'warn',
Expand Down
40 changes: 40 additions & 0 deletionspackages/eslint-plugin/src/rules/enum-utils/shared.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
import * as tsutils from 'tsutils';
import * as ts from 'typescript';

import * as util from '../../util';

/*
* If passed an enum member, returns the type of the parent. Otherwise,
* returns itself.
*
* For example:
* - `Fruit` --> `Fruit`
* - `Fruit.Apple` --> `Fruit`
*/
function getBaseEnumType(typeChecker: ts.TypeChecker, type: ts.Type): ts.Type {
const symbol = type.getSymbol()!;
if (!tsutils.isSymbolFlagSet(symbol, ts.SymbolFlags.EnumMember)) {
return type;
}

return typeChecker.getTypeAtLocation(symbol.valueDeclaration!.parent);
}

/**
* A type can have 0 or more enum types. For example:
* - 123 --> []
* - {} --> []
* - Fruit.Apple --> [Fruit]
* - Fruit.Apple | Vegetable.Lettuce --> [Fruit, Vegetable]
* - Fruit.Apple | Vegetable.Lettuce | 123 --> [Fruit, Vegetable]
* - T extends Fruit --> [Fruit]
*/
export function getEnumTypes(
typeChecker: ts.TypeChecker,
type: ts.Type,
): ts.Type[] {
return tsutils
.unionTypeParts(type)
.filter(subType => util.isTypeFlagSet(subType, ts.TypeFlags.EnumLiteral))
.map(type => getBaseEnumType(typeChecker, type));
}
2 changes: 2 additions & 0 deletionspackages/eslint-plugin/src/rules/index.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -85,6 +85,7 @@ import noUnsafeArgument from './no-unsafe-argument';
import noUnsafeAssignment from './no-unsafe-assignment';
import noUnsafeCall from './no-unsafe-call';
import noUnsafeDeclarationMerging from './no-unsafe-declaration-merging';
import noUnsafeEnumComparison from './no-unsafe-enum-comparison';
import noUnsafeMemberAccess from './no-unsafe-member-access';
import noUnsafeReturn from './no-unsafe-return';
import noUnusedExpressions from './no-unused-expressions';
Expand DownExpand Up@@ -222,6 +223,7 @@ export default {
'no-unsafe-assignment': noUnsafeAssignment,
'no-unsafe-call': noUnsafeCall,
'no-unsafe-declaration-merging': noUnsafeDeclarationMerging,
'no-unsafe-enum-comparison': noUnsafeEnumComparison,
'no-unsafe-member-access': noUnsafeMemberAccess,
'no-unsafe-return': noUnsafeReturn,
'no-unused-expressions': noUnusedExpressions,
Expand Down
121 changes: 121 additions & 0 deletionspackages/eslint-plugin/src/rules/no-unsafe-enum-comparison.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
import type { TSESTree } from '@typescript-eslint/utils';
import * as tsutils from 'tsutils';
import * as ts from 'typescript';

import * as util from '../util';
import { getEnumTypes } from './enum-utils/shared';

/**
* @returns Whether the right type is an unsafe comparison against any left type.
*/
function typeViolates(leftTypeParts: ts.Type[], right: ts.Type): boolean {
const leftValueKinds = new Set(leftTypeParts.map(getEnumValueType));

return (
(leftValueKinds.has(ts.TypeFlags.Number) &&
tsutils.isTypeFlagSet(
right,
ts.TypeFlags.Number | ts.TypeFlags.NumberLike,
)) ||
(leftValueKinds.has(ts.TypeFlags.String) &&
tsutils.isTypeFlagSet(
right,
ts.TypeFlags.String | ts.TypeFlags.StringLike,
))
);
}

/**
* @returns What type a type's enum value is (number or string), if either.
*/
function getEnumValueType(type: ts.Type): ts.TypeFlags | undefined {
return util.isTypeFlagSet(type, ts.TypeFlags.EnumLike)
? util.isTypeFlagSet(type, ts.TypeFlags.NumberLiteral)
? ts.TypeFlags.Number
: ts.TypeFlags.String
: undefined;
}

export default util.createRule({
name: 'no-unsafe-enum-comparison',
meta: {
type: 'suggestion',
docs: {
description: 'Disallow comparing an enum value with a non-enum value',
recommended: 'strict',
requiresTypeChecking: true,
},
messages: {
mismatched:
'The two values in this comparison do not have a shared enum type.',
},
schema: [],
},
defaultOptions: [],
create(context) {
const parserServices = util.getParserServices(context);
const typeChecker = parserServices.program.getTypeChecker();

function getTypeFromNode(node: TSESTree.Node): ts.Type {
return typeChecker.getTypeAtLocation(
parserServices.esTreeNodeToTSNodeMap.get(node),
);
}

return {
'BinaryExpression[operator=/=|<|>/]'(
node: TSESTree.BinaryExpression,
): void {
const left = getTypeFromNode(node.left);
const right = getTypeFromNode(node.right);

// Allow comparisons that don't have anything to do with enums:
//
// ```ts
// 1 === 2;
// ```
const leftEnumTypes = getEnumTypes(typeChecker, left);
const rightEnumTypes = new Set(getEnumTypes(typeChecker, right));
if (leftEnumTypes.length === 0 && rightEnumTypes.size === 0) {
return;
}

// Allow comparisons that share an enum type:
//
// ```ts
// Fruit.Apple === Fruit.Banana;
// ```
for (const leftEnumType of leftEnumTypes) {
if (rightEnumTypes.has(leftEnumType)) {
return;
}
}

const leftTypeParts = tsutils.unionTypeParts(left);
const rightTypeParts = tsutils.unionTypeParts(right);

// If a type exists in both sides, we consider this comparison safe:
//
// ```ts
// declare const fruit: Fruit.Apple | 0;
// fruit === 0;
// ```
for (const leftTypePart of leftTypeParts) {
if (rightTypeParts.includes(leftTypePart)) {
return;
}
}

if (
typeViolates(leftTypeParts, right) ||
typeViolates(rightTypeParts, left)
) {
context.report({
messageId: 'mismatched',
node,
});
}
},
};
},
});
Loading

[8]ページ先頭

©2009-2025 Movatter.jp