Skip to content

⬅️ Back to Table of Contents

πŸ“„ prefer-literal-enum-member

πŸ“Š Analysis Summary

Metric Count
πŸ”§ Functions 5
πŸ“¦ Imports 4

πŸ“š Table of Contents

πŸ› οΈ File Location:

πŸ“‚ packages/eslint-plugin/src/rules/prefer-literal-enum-member.ts

πŸ“€ Default Export

export default createRule({ ... })
Property Value
name 'prefer-literal-enum-member'
meta.type 'suggestion'
meta.docs.description 'Require all enum members to be literal values'
meta.docs.recommended 'strict'
meta.docs.requiresTypeChecking false
meta.messages.notLiteral Explicit enum value must only be a literal value (string or number).
meta.messages.notLiteralOrBitwiseExpression Explicit enum value must only be a literal value (string or number) or a bitwise expression.
meta.schema [ { type: 'object', additionalProperties: false, properties: { allowBitwiseExpressions: { type: 'boolean', descriptio...
defaultOptions [ { allowBitwiseExpressions: false, }, ]

Entry point: create β€” documented under Functions.


πŸ“¦ Imports

Name Source
TSESTree @typescript-eslint/utils
AST_NODE_TYPES @typescript-eslint/utils
createRule ../util
getStaticStringValue ../util

Functions

create(context: any, [{ allowBitwiseExpressions }]: any): { TSEnumMember(node: any): void; }

Parameters:

  • context any
  • [{ allowBitwiseExpressions }] any

Returns: { TSEnumMember(node: any): void; }

Calls:

  • decl.body.members.some
  • isIdentifierWithName
  • getStaticStringValue (from ../util)
  • hasEnumMember
  • isSelfEnumMember
  • ['-', '+'].includes
  • isAllowedInitializerExpressionRecursive
  • ['&', '^', '<<', '>>', '>>>', '|'].includes
  • context.report

Internal Comments:

// If there is no initializer, then this node is just the name of the member, so ignore.
// You can only refer to an enum member if it's part of a bitwise computation.
// so C = B isn't allowed (special case), but C = A | B is.
// any old literal
// TemplateLiteral without expressions
// +123, -123, etc.

Code
create(context, [{ allowBitwiseExpressions }]) {
    function isIdentifierWithName(node: TSESTree.Node, name: string): boolean {
      return node.type === AST_NODE_TYPES.Identifier && node.name === name;
    }

    function hasEnumMember(
      decl: TSESTree.TSEnumDeclaration,
      name: string,
    ): boolean {
      return decl.body.members.some(
        member =>
          isIdentifierWithName(member.id, name) ||
          (member.id.type === AST_NODE_TYPES.Literal &&
            getStaticStringValue(member.id) === name),
      );
    }

    function isSelfEnumMember(
      decl: TSESTree.TSEnumDeclaration,
      node: TSESTree.Node,
    ): boolean {
      if (node.type === AST_NODE_TYPES.Identifier) {
        return hasEnumMember(decl, node.name);
      }

      if (
        node.type === AST_NODE_TYPES.MemberExpression &&
        isIdentifierWithName(node.object, decl.id.name)
      ) {
        if (node.property.type === AST_NODE_TYPES.Identifier) {
          return hasEnumMember(decl, node.property.name);
        }

        if (node.computed) {
          const propertyName = getStaticStringValue(node.property);
          if (propertyName) {
            return hasEnumMember(decl, propertyName);
          }
        }
      }
      return false;
    }

    return {
      TSEnumMember(node): void {
        // If there is no initializer, then this node is just the name of the member, so ignore.
        if (node.initializer == null) {
          return;
        }
        const declaration = node.parent.parent;

        function isAllowedInitializerExpressionRecursive(
          node: TSESTree.Expression | TSESTree.PrivateIdentifier,
          partOfBitwiseComputation: boolean,
        ): boolean {
          // You can only refer to an enum member if it's part of a bitwise computation.
          // so C = B isn't allowed (special case), but C = A | B is.
          if (partOfBitwiseComputation && isSelfEnumMember(declaration, node)) {
            return true;
          }

          switch (node.type) {
            // any old literal
            case AST_NODE_TYPES.Literal:
              return true;

            // TemplateLiteral without expressions
            case AST_NODE_TYPES.TemplateLiteral:
              return node.expressions.length === 0;

            case AST_NODE_TYPES.UnaryExpression:
              // +123, -123, etc.
              if (['-', '+'].includes(node.operator)) {
                return isAllowedInitializerExpressionRecursive(
                  node.argument,
                  partOfBitwiseComputation,
                );
              }

              if (allowBitwiseExpressions) {
                return (
                  node.operator === '~' &&
                  isAllowedInitializerExpressionRecursive(node.argument, true)
                );
              }
              return false;

            case AST_NODE_TYPES.BinaryExpression:
              if (allowBitwiseExpressions) {
                return (
                  ['&', '^', '<<', '>>', '>>>', '|'].includes(node.operator) &&
                  isAllowedInitializerExpressionRecursive(node.left, true) &&
                  isAllowedInitializerExpressionRecursive(node.right, true)
                );
              }
              return false;

            default:
              return false;
          }
        }

        if (isAllowedInitializerExpressionRecursive(node.initializer, false)) {
          return;
        }

        context.report({
          node: node.id,
          messageId: allowBitwiseExpressions
            ? 'notLiteralOrBitwiseExpression'
            : 'notLiteral',
        });
      },
    };
  }

Internal helpers

Declared inside another function in this file.

isIdentifierWithName(node: TSESTree.Node, name: string): boolean

Parameters:

  • node TSESTree.Node
  • name string

Returns: boolean

Code
function isIdentifierWithName(node: TSESTree.Node, name: string): boolean {
      return node.type === AST_NODE_TYPES.Identifier && node.name === name;
    }

hasEnumMember(decl: TSESTree.TSEnumDeclaration, name: string): boolean

Parameters:

  • decl TSESTree.TSEnumDeclaration
  • name string

Returns: boolean

Calls:

  • decl.body.members.some
  • isIdentifierWithName
  • getStaticStringValue (from ../util)
Code
function hasEnumMember(
      decl: TSESTree.TSEnumDeclaration,
      name: string,
    ): boolean {
      return decl.body.members.some(
        member =>
          isIdentifierWithName(member.id, name) ||
          (member.id.type === AST_NODE_TYPES.Literal &&
            getStaticStringValue(member.id) === name),
      );
    }

isSelfEnumMember(decl: TSESTree.TSEnumDeclaration, node: TSESTree.Node): boolean

Parameters:

  • decl TSESTree.TSEnumDeclaration
  • node TSESTree.Node

Returns: boolean

Calls:

  • hasEnumMember
  • isIdentifierWithName
  • getStaticStringValue (from ../util)
Code
function isSelfEnumMember(
      decl: TSESTree.TSEnumDeclaration,
      node: TSESTree.Node,
    ): boolean {
      if (node.type === AST_NODE_TYPES.Identifier) {
        return hasEnumMember(decl, node.name);
      }

      if (
        node.type === AST_NODE_TYPES.MemberExpression &&
        isIdentifierWithName(node.object, decl.id.name)
      ) {
        if (node.property.type === AST_NODE_TYPES.Identifier) {
          return hasEnumMember(decl, node.property.name);
        }

        if (node.computed) {
          const propertyName = getStaticStringValue(node.property);
          if (propertyName) {
            return hasEnumMember(decl, propertyName);
          }
        }
      }
      return false;
    }

isAllowedInitializerExpressionRecursive(node: TSESTree.Expression | TSESTree.PrivateI…, partOfBitwiseComputation: boolean): boolean

Parameters:

  • node TSESTree.Expression | TSESTree.PrivateIdentifier
  • partOfBitwiseComputation boolean

Returns: boolean

Calls:

  • isSelfEnumMember
  • ['-', '+'].includes
  • isAllowedInitializerExpressionRecursive
  • ['&', '^', '<<', '>>', '>>>', '|'].includes

Internal Comments:

// You can only refer to an enum member if it's part of a bitwise computation.
// so C = B isn't allowed (special case), but C = A | B is.
// any old literal
// TemplateLiteral without expressions
// +123, -123, etc.

Code
function isAllowedInitializerExpressionRecursive(
          node: TSESTree.Expression | TSESTree.PrivateIdentifier,
          partOfBitwiseComputation: boolean,
        ): boolean {
          // You can only refer to an enum member if it's part of a bitwise computation.
          // so C = B isn't allowed (special case), but C = A | B is.
          if (partOfBitwiseComputation && isSelfEnumMember(declaration, node)) {
            return true;
          }

          switch (node.type) {
            // any old literal
            case AST_NODE_TYPES.Literal:
              return true;

            // TemplateLiteral without expressions
            case AST_NODE_TYPES.TemplateLiteral:
              return node.expressions.length === 0;

            case AST_NODE_TYPES.UnaryExpression:
              // +123, -123, etc.
              if (['-', '+'].includes(node.operator)) {
                return isAllowedInitializerExpressionRecursive(
                  node.argument,
                  partOfBitwiseComputation,
                );
              }

              if (allowBitwiseExpressions) {
                return (
                  node.operator === '~' &&
                  isAllowedInitializerExpressionRecursive(node.argument, true)
                );
              }
              return false;

            case AST_NODE_TYPES.BinaryExpression:
              if (allowBitwiseExpressions) {
                return (
                  ['&', '^', '<<', '>>', '>>>', '|'].includes(node.operator) &&
                  isAllowedInitializerExpressionRecursive(node.left, true) &&
                  isAllowedInitializerExpressionRecursive(node.right, true)
                );
              }
              return false;

            default:
              return false;
          }
        }

Generated by Syntax Scribe