-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
feat(eslint-plugin): [no-unsafe-enum-comparison] add switch suggestion #7691
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
Changes from all commits
0471e12
930b14a
b800d1b
bbf8156
d901495
1efbc51
b49c446
eb577f5
2e7619e
42928a5
32c3d75
3a923ca
a20fe15
07f9306
f62bd56
9c492f7
7ce6387
cf56040
acea5f6
52af550
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,23 @@ function getBaseEnumType(typeChecker: ts.TypeChecker, type: ts.Type): ts.Type { | |
return typeChecker.getTypeAtLocation(symbol.valueDeclaration!.parent); | ||
} | ||
|
||
/** | ||
* Retrieve only the Enum literals from a type. for example: | ||
* - 123 --> [] | ||
* - {} --> [] | ||
* - Fruit.Apple --> [Fruit.Apple] | ||
* - Fruit.Apple | Vegetable.Lettuce --> [Fruit.Apple, Vegetable.Lettuce] | ||
* - Fruit.Apple | Vegetable.Lettuce | 123 --> [Fruit.Apple, Vegetable.Lettuce] | ||
* - T extends Fruit --> [Fruit] | ||
*/ | ||
export function getEnumLiterals(type: ts.Type): ts.LiteralType[] { | ||
return tsutils | ||
.unionTypeParts(type) | ||
.filter((subType): subType is ts.LiteralType => | ||
isTypeFlagSet(subType, ts.TypeFlags.EnumLiteral), | ||
); | ||
} | ||
|
||
/** | ||
* A type can have 0 or more enum types. For example: | ||
* - 123 --> [] | ||
|
@@ -33,8 +50,55 @@ export function getEnumTypes( | |
typeChecker: ts.TypeChecker, | ||
type: ts.Type, | ||
): ts.Type[] { | ||
return tsutils | ||
.unionTypeParts(type) | ||
.filter(subType => isTypeFlagSet(subType, ts.TypeFlags.EnumLiteral)) | ||
.map(type => getBaseEnumType(typeChecker, type)); | ||
return getEnumLiterals(type).map(type => getBaseEnumType(typeChecker, type)); | ||
} | ||
|
||
/** | ||
* Returns the enum key that matches the given literal node, or null if none | ||
* match. For example: | ||
* ```ts | ||
* enum Fruit { | ||
* Apple = 'apple', | ||
* Banana = 'banana', | ||
* } | ||
* | ||
* getEnumKeyForLiteral([Fruit.Apple, Fruit.Banana], 'apple') --> 'Fruit.Apple' | ||
* getEnumKeyForLiteral([Fruit.Apple, Fruit.Banana], 'banana') --> 'Fruit.Banana' | ||
* getEnumKeyForLiteral([Fruit.Apple, Fruit.Banana], 'cherry') --> null | ||
JoshuaKGoldberg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* ``` | ||
*/ | ||
export function getEnumKeyForLiteral( | ||
enumLiterals: ts.LiteralType[], | ||
literal: unknown, | ||
): string | null { | ||
for (const enumLiteral of enumLiterals) { | ||
if (enumLiteral.value === literal) { | ||
const { symbol } = enumLiteral; | ||
|
||
const memberDeclaration = symbol.valueDeclaration as ts.EnumMember; | ||
const enumDeclaration = memberDeclaration.parent; | ||
|
||
const memberNameIdentifier = memberDeclaration.name; | ||
const enumName = enumDeclaration.name.text; | ||
|
||
switch (memberNameIdentifier.kind) { | ||
case ts.SyntaxKind.Identifier: | ||
return `${enumName}.${memberNameIdentifier.text}`; | ||
|
||
case ts.SyntaxKind.StringLiteral: { | ||
const memberName = memberNameIdentifier.text.replace(/'/g, "\\'"); | ||
|
||
return `${enumName}['${memberName}']`; | ||
} | ||
|
||
case ts.SyntaxKind.ComputedPropertyName: | ||
return `${enumName}[${memberNameIdentifier.expression.getText()}]`; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. had to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The type of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would probably keep it like this, seems fine in the tests (unless I'm missing some cases?) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The existing bugs in #7768 make me not too motivated to get every single possible edge case in this PR. The tests you've added are pretty great. Thanks! |
||
|
||
default: | ||
break; | ||
} | ||
} | ||
} | ||
|
||
return null; | ||
} |
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.
[Praise] Great comments, thanks for continuing the existing standard 🙂