diff --git a/README.md b/README.md index 1795e85..f3a1310 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,14 @@ Why? To maintain a coherent user experience, we aim to use the classes and components from the shared component library as much as possible, as opposed to custom styles. +### no-indexed-access-on-enums + +_Forbid accessing an enum thanks to an index: Enum[0]._ + +Why? + +The names of the enum values should not be "data" in the context of the application at runtime. This brings a lack of clarity, a lack of readability, maintenance issues, and can be error-prone. + ### no-ngxs-select-decorator _Forbid using the NGXS `@Select()` decorator: `@ViewSelectSnapshot()` should be preferred._ @@ -218,7 +226,7 @@ When using `.reduce()`, it may be tempting to do something like this for the sak const mappedById = myArray.reduce((acc, entity) => ({ ...acc, [entity.id]: entity }), {}); ``` -However, spreading the accumulator at every iteration results in an operation with O(n^2) time & spatial complexity. +However, spreading the accumulator at every iteration results in an operation with O(n^2) time & spatial complexity. This rule helps ensure that `.reduce()` is an O(n) operation. For example: diff --git a/lib/configs/internal/recommended.js b/lib/configs/internal/recommended.js index e978f66..8dfd796 100644 --- a/lib/configs/internal/recommended.js +++ b/lib/configs/internal/recommended.js @@ -14,6 +14,7 @@ module.exports = { 'criteo/filename-match-export': ['error', { removeFromFilename: ['.actions'] }], 'criteo/no-null-undefined-comparison': 'error', 'criteo/no-spreading-accumulators': 'error', + 'criteo/no-indexed-access-on-enums': 'error', 'criteo/no-todo-without-ticket': 'error', 'eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }], 'eslint-comments/require-description': 'error', diff --git a/lib/rules/no-indexed-access-on-enums.js b/lib/rules/no-indexed-access-on-enums.js new file mode 100644 index 0000000..e91c020 --- /dev/null +++ b/lib/rules/no-indexed-access-on-enums.js @@ -0,0 +1,48 @@ +/** + * @fileoverview no-indexed-access-on-enums + * @author Connor Ullman + * @author Adam Perea + */ +'use strict'; + +module.exports = { + meta: { + type: 'suggestion', + docs: { + description: 'Do not use indexed access on enums', + recommended: 'error', + }, + messages: { + failure: 'Indexed accesses on an enum are almost always a mistake, as the names of the enum values should not be "data" in the context of the application at runtime.', + }, + schema: [], + }, + + create(context) { + return { + MemberExpression(node) { + const parserServices = context.parserServices; + if (!parserServices) return; + + const checker = parserServices.program?.getTypeChecker(); + if (!checker) return; + + if (!node.computed) return; + + const objectNode = parserServices.esTreeNodeToTSNodeMap.get(node.object); + const objectSymbol = checker.getSymbolAtLocation(objectNode); + if (!objectSymbol) return; + + const enumSymbol = + ts.SymbolFlags.Alias & objectSymbol.flags ? checker.getAliasedSymbol(objectSymbol) : objectSymbol; + const isEnum = ts.SymbolFlags.Enum & enumSymbol.flags; + if (!isEnum) return; + + context.report({ + node, + messageId: 'failure', + }); + }, + }; + }, +};