Skip to content

⬅️ Back to Table of Contents

📄 prefer-tsutils-methods

📊 Analysis Summary

Metric Count
🔧 Functions 6
📦 Imports 3
📊 Variables & Constants 1
📑 Type Aliases 1

📚 Table of Contents

🛠️ File Location:

📂 packages/eslint-plugin-internal/src/rules/prefer-tsutils-methods.ts

📤 Default Export

export default createRule({ ... })
Property Value
name 'prefer-tsutils-methods'
meta.type 'problem'
meta.docs.description 'Enforce using ts-api-utils methods over direct bitwise flag checks'
meta.fixable 'code'
meta.messages.preferMethod 'Prefer tsutils.{{ method }}() over bitwise {{ flagType }} check'
meta.schema []
defaultOptions []

Entry point: create — documented under Functions.


📦 Imports

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

Variables & Constants

Name Type Kind Value Exported
FLAG_CONFIGS { ObjectFlags: { flagsProperty: strin... const { ObjectFlags: { flagsProperty: 'objectFlags', method: 'isObjectFlagSet', }, ...

Functions

create(context: any): { BinaryExpression(node: TSESTree.BinaryExpression): void; }

Parameters:

  • context any

Returns: { BinaryExpression(node: TSESTree.BinaryExpression): void; }

Calls:

  • isFlagType
  • context.sourceCode.getText
  • isTsFlagAccess
  • isFlagOrExpression
  • isFlagPropertyAccess
  • analyzeBitwiseAnd
  • context.report
  • fixer.replaceText
Code
create(context) {
    function isTsFlagAccess(node: TSESTree.Node) {
      if (
        node.type === AST_NODE_TYPES.MemberExpression &&
        node.object.type === AST_NODE_TYPES.MemberExpression &&
        node.object.object.type === AST_NODE_TYPES.Identifier &&
        node.object.object.name === 'ts' &&
        node.object.property.type === AST_NODE_TYPES.Identifier
      ) {
        const flagType = node.object.property.name;
        if (isFlagType(flagType)) {
          return { flagType, fullText: context.sourceCode.getText(node) };
        }
      }
      return null;
    }

    function isFlagOrExpression(
      node: TSESTree.Node,
    ): { flagType: FlagType; fullText: string } | null {
      if (
        node.type === AST_NODE_TYPES.BinaryExpression &&
        node.operator === '|'
      ) {
        const leftFlag = isTsFlagAccess(node.left);
        if (leftFlag) {
          return {
            flagType: leftFlag.flagType,
            fullText: context.sourceCode.getText(node),
          };
        }
        const leftOr = isFlagOrExpression(node.left);
        if (leftOr) {
          return {
            flagType: leftOr.flagType,
            fullText: context.sourceCode.getText(node),
          };
        }
      }
      return null;
    }

    function isFlagPropertyAccess(
      node: TSESTree.Node,
      expectedFlagType: FlagType,
    ) {
      if (
        node.type === AST_NODE_TYPES.MemberExpression &&
        node.property.type === AST_NODE_TYPES.Identifier
      ) {
        const propName = node.property.name;
        const expectedPropName = FLAG_CONFIGS[expectedFlagType].flagsProperty;
        if (propName === expectedPropName) {
          return {
            objectText: context.sourceCode.getText(node.object),
          };
        }
      }
      return null;
    }

    function analyzeBitwiseAnd(node: TSESTree.BinaryExpression) {
      const { left, right } = node;

      const rightFlag = isTsFlagAccess(right) ?? isFlagOrExpression(right);
      if (rightFlag) {
        const leftPropAccess = isFlagPropertyAccess(left, rightFlag.flagType);
        if (leftPropAccess) {
          return {
            flagText: rightFlag.fullText,
            flagType: rightFlag.flagType,
            objectText: leftPropAccess.objectText,
          };
        }
      }

      const leftFlag = isTsFlagAccess(left) ?? isFlagOrExpression(left);
      if (leftFlag) {
        const rightPropAccess = isFlagPropertyAccess(right, leftFlag.flagType);
        if (rightPropAccess) {
          return {
            flagText: leftFlag.fullText,
            flagType: leftFlag.flagType,
            objectText: rightPropAccess.objectText,
          };
        }
      }

      return null;
    }

    return {
      BinaryExpression(node: TSESTree.BinaryExpression): void {
        if (node.operator !== '&') {
          return;
        }

        const analysis = analyzeBitwiseAnd(node);
        if (!analysis) {
          return;
        }

        const { flagText, flagType, objectText } = analysis;
        const method = FLAG_CONFIGS[flagType].method;

        let nodeToReplace: TSESTree.Node = node;
        let isNegated = false;

        if (
          node.parent.type === AST_NODE_TYPES.BinaryExpression &&
          (node.parent.operator === '===' || node.parent.operator === '!==') &&
          node.parent.right.type === AST_NODE_TYPES.Literal &&
          node.parent.right.value === 0
        ) {
          nodeToReplace = node.parent;
          isNegated = node.parent.operator === '===';
        }

        const replacement = isNegated
          ? `!tsutils.${method}(${objectText}, ${flagText})`
          : `tsutils.${method}(${objectText}, ${flagText})`;

        context.report({
          node: nodeToReplace,
          messageId: 'preferMethod',
          data: { flagType, method },
          fix: fixer => fixer.replaceText(nodeToReplace, replacement),
        });
      },
    };
  }

isFlagType(name: string): name is FlagType

Parameters:

  • name string

Returns: name is FlagType

Code
function isFlagType(name: string): name is FlagType {
  return (
    name === 'TypeFlags' || name === 'SymbolFlags' || name === 'ObjectFlags'
  );
}

Internal helpers

Declared inside another function in this file.

isTsFlagAccess(node: TSESTree.Node): { flagType: FlagType; fullText: any; }

Parameters:

  • node TSESTree.Node

Returns: { flagType: FlagType; fullText: any; }

Calls:

  • isFlagType
  • context.sourceCode.getText
Code
function isTsFlagAccess(node: TSESTree.Node) {
      if (
        node.type === AST_NODE_TYPES.MemberExpression &&
        node.object.type === AST_NODE_TYPES.MemberExpression &&
        node.object.object.type === AST_NODE_TYPES.Identifier &&
        node.object.object.name === 'ts' &&
        node.object.property.type === AST_NODE_TYPES.Identifier
      ) {
        const flagType = node.object.property.name;
        if (isFlagType(flagType)) {
          return { flagType, fullText: context.sourceCode.getText(node) };
        }
      }
      return null;
    }

isFlagOrExpression(node: TSESTree.Node): { flagType: FlagType; fullText: string } | null

Parameters:

  • node TSESTree.Node

Returns: { flagType: FlagType; fullText: string } | null

Calls:

  • isTsFlagAccess
  • context.sourceCode.getText
  • isFlagOrExpression
Code
function isFlagOrExpression(
      node: TSESTree.Node,
    ): { flagType: FlagType; fullText: string } | null {
      if (
        node.type === AST_NODE_TYPES.BinaryExpression &&
        node.operator === '|'
      ) {
        const leftFlag = isTsFlagAccess(node.left);
        if (leftFlag) {
          return {
            flagType: leftFlag.flagType,
            fullText: context.sourceCode.getText(node),
          };
        }
        const leftOr = isFlagOrExpression(node.left);
        if (leftOr) {
          return {
            flagType: leftOr.flagType,
            fullText: context.sourceCode.getText(node),
          };
        }
      }
      return null;
    }

isFlagPropertyAccess(node: TSESTree.Node, expectedFlagType: FlagType): { objectText: any; }

Parameters:

  • node TSESTree.Node
  • expectedFlagType FlagType

Returns: { objectText: any; }

Calls:

  • context.sourceCode.getText
Code
function isFlagPropertyAccess(
      node: TSESTree.Node,
      expectedFlagType: FlagType,
    ) {
      if (
        node.type === AST_NODE_TYPES.MemberExpression &&
        node.property.type === AST_NODE_TYPES.Identifier
      ) {
        const propName = node.property.name;
        const expectedPropName = FLAG_CONFIGS[expectedFlagType].flagsProperty;
        if (propName === expectedPropName) {
          return {
            objectText: context.sourceCode.getText(node.object),
          };
        }
      }
      return null;
    }

analyzeBitwiseAnd(node: TSESTree.BinaryExpression): { flagText: any; flagType: FlagType; objectText: any; }

Parameters:

  • node TSESTree.BinaryExpression

Returns: { flagText: any; flagType: FlagType; objectText: any; }

Calls:

  • isTsFlagAccess
  • isFlagOrExpression
  • isFlagPropertyAccess
Code
function analyzeBitwiseAnd(node: TSESTree.BinaryExpression) {
      const { left, right } = node;

      const rightFlag = isTsFlagAccess(right) ?? isFlagOrExpression(right);
      if (rightFlag) {
        const leftPropAccess = isFlagPropertyAccess(left, rightFlag.flagType);
        if (leftPropAccess) {
          return {
            flagText: rightFlag.fullText,
            flagType: rightFlag.flagType,
            objectText: leftPropAccess.objectText,
          };
        }
      }

      const leftFlag = isTsFlagAccess(left) ?? isFlagOrExpression(left);
      if (leftFlag) {
        const rightPropAccess = isFlagPropertyAccess(right, leftFlag.flagType);
        if (rightPropAccess) {
          return {
            flagText: leftFlag.fullText,
            flagType: leftFlag.flagType,
            objectText: rightPropAccess.objectText,
          };
        }
      }

      return null;
    }

Type Aliases

FlagType

type FlagType = 'ObjectFlags' | 'SymbolFlags' | 'TypeFlags';

Generated by Syntax Scribe