Skip to content

⬅️ Back to Table of Contents

📄 no-unnecessary-qualifier.ts

📊 Analysis Summary

Metric Count
🔧 Functions 11
📦 Imports 4
📊 Variables & Constants 5

📚 Table of Contents

🛠️ File Location:

📂 packages/eslint-plugin/src/rules/no-unnecessary-qualifier.ts

📦 Imports

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

Variables & Constants

Name Type Kind Value Exported
namespacesInScope ts.Node[] const []
currentFailedNamespaceExpression TSESTree.Node | null let/var null
esTreeNodeToTSNodeMap any const services.esTreeNodeToTSNodeMap
symbolDeclarations any const symbol.getDeclarations() ?? []
property TSESTree.Identifier const node.property as TSESTree.Identifier

Functions

tryGetAliasedSymbol(symbol: ts.Symbol, checker: ts.TypeChecker): ts.Symbol | null

Code
function tryGetAliasedSymbol(
      symbol: ts.Symbol,
      checker: ts.TypeChecker,
    ): ts.Symbol | null {
      return tsutils.isSymbolFlagSet(symbol, ts.SymbolFlags.Alias)
        ? checker.getAliasedSymbol(symbol)
        : null;
    }
  • Parameters:
  • symbol: ts.Symbol
  • checker: ts.TypeChecker
  • Return Type: ts.Symbol | null
  • Calls:
  • tsutils.isSymbolFlagSet
  • checker.getAliasedSymbol

symbolIsNamespaceInScope(symbol: ts.Symbol): boolean

Code
function symbolIsNamespaceInScope(symbol: ts.Symbol): boolean {
      const symbolDeclarations = symbol.getDeclarations() ?? [];

      if (
        symbolDeclarations.some(decl =>
          namespacesInScope.some(ns => ns === decl),
        )
      ) {
        return true;
      }

      const alias = tryGetAliasedSymbol(symbol, checker);

      return alias != null && symbolIsNamespaceInScope(alias);
    }
  • Parameters:
  • symbol: ts.Symbol
  • Return Type: boolean
  • Calls:
  • symbol.getDeclarations
  • symbolDeclarations.some
  • namespacesInScope.some
  • tryGetAliasedSymbol
  • symbolIsNamespaceInScope

getSymbolInScope(node: ts.Node, flags: ts.SymbolFlags, name: string): ts.Symbol | undefined

Code
function getSymbolInScope(
      node: ts.Node,
      flags: ts.SymbolFlags,
      name: string,
    ): ts.Symbol | undefined {
      const scope = checker.getSymbolsInScope(node, flags);

      return scope.find(scopeSymbol => scopeSymbol.name === name);
    }
  • Parameters:
  • node: ts.Node
  • flags: ts.SymbolFlags
  • name: string
  • Return Type: ts.Symbol | undefined
  • Calls:
  • checker.getSymbolsInScope
  • scope.find

symbolsAreEqual(accessed: ts.Symbol, inScope: ts.Symbol): boolean

Code
function symbolsAreEqual(accessed: ts.Symbol, inScope: ts.Symbol): boolean {
      return accessed === checker.getExportSymbolOfSymbol(inScope);
    }
  • Parameters:
  • accessed: ts.Symbol
  • inScope: ts.Symbol
  • Return Type: boolean
  • Calls:
  • checker.getExportSymbolOfSymbol

qualifierIsUnnecessary(qualifier: TSESTree.EntityName | TSESTree.MemberExpression, name: TSESTree.Identifier): boolean

Code
function qualifierIsUnnecessary(
      qualifier: TSESTree.EntityName | TSESTree.MemberExpression,
      name: TSESTree.Identifier,
    ): boolean {
      const namespaceSymbol = services.getSymbolAtLocation(qualifier);

      if (
        namespaceSymbol == null ||
        !symbolIsNamespaceInScope(namespaceSymbol)
      ) {
        return false;
      }

      const accessedSymbol = services.getSymbolAtLocation(name);

      if (accessedSymbol == null) {
        return false;
      }

      // If the symbol in scope is different, the qualifier is necessary.
      const tsQualifier = esTreeNodeToTSNodeMap.get(qualifier);
      const fromScope = getSymbolInScope(
        tsQualifier,
        accessedSymbol.flags,
        context.sourceCode.getText(name),
      );

      return !!fromScope && symbolsAreEqual(accessedSymbol, fromScope);
    }
  • Parameters:
  • qualifier: TSESTree.EntityName | TSESTree.MemberExpression
  • name: TSESTree.Identifier
  • Return Type: boolean
  • Calls:
  • services.getSymbolAtLocation
  • symbolIsNamespaceInScope
  • esTreeNodeToTSNodeMap.get
  • getSymbolInScope
  • context.sourceCode.getText
  • symbolsAreEqual
  • Internal Comments:
    // If the symbol in scope is different, the qualifier is necessary. (x2)
    

visitNamespaceAccess(node: TSESTree.Node, qualifier: TSESTree.EntityName | TSESTree.MemberExpression, name: TSESTree.Identifier): void

Code
function visitNamespaceAccess(
      node: TSESTree.Node,
      qualifier: TSESTree.EntityName | TSESTree.MemberExpression,
      name: TSESTree.Identifier,
    ): void {
      // Only look for nested qualifier errors if we didn't already fail on the outer qualifier.
      if (
        !currentFailedNamespaceExpression &&
        qualifierIsUnnecessary(qualifier, name)
      ) {
        currentFailedNamespaceExpression = node;
        context.report({
          node: qualifier,
          messageId: 'unnecessaryQualifier',
          data: {
            name: context.sourceCode.getText(name),
          },
          fix(fixer) {
            return fixer.removeRange([qualifier.range[0], name.range[0]]);
          },
        });
      }
    }
  • Parameters:
  • node: TSESTree.Node
  • qualifier: TSESTree.EntityName | TSESTree.MemberExpression
  • name: TSESTree.Identifier
  • Return Type: void
  • Calls:
  • qualifierIsUnnecessary
  • context.report
  • context.sourceCode.getText
  • fixer.removeRange
  • Internal Comments:
    // Only look for nested qualifier errors if we didn't already fail on the outer qualifier.
    

`enterDeclaration(node: | TSESTree.ExportNamedDeclaration

    | TSESTree.TSEnumDeclaration
    | TSESTree.TSModuleDeclaration): void`
Code
function enterDeclaration(
      node:
        | TSESTree.ExportNamedDeclaration
        | TSESTree.TSEnumDeclaration
        | TSESTree.TSModuleDeclaration,
    ): void {
      namespacesInScope.push(esTreeNodeToTSNodeMap.get(node));
    }
  • Parameters:
  • node: | TSESTree.ExportNamedDeclaration | TSESTree.TSEnumDeclaration | TSESTree.TSModuleDeclaration
  • Return Type: void
  • Calls:
  • namespacesInScope.push
  • esTreeNodeToTSNodeMap.get

exitDeclaration(): void

Code
function exitDeclaration(): void {
      namespacesInScope.pop();
    }
  • Return Type: void
  • Calls:
  • namespacesInScope.pop

resetCurrentNamespaceExpression(node: TSESTree.Node): void

Code
function resetCurrentNamespaceExpression(node: TSESTree.Node): void {
      if (node === currentFailedNamespaceExpression) {
        currentFailedNamespaceExpression = null;
      }
    }
  • Parameters:
  • node: TSESTree.Node
  • Return Type: void

isPropertyAccessExpression(node: TSESTree.Node): node is TSESTree.MemberExpression

Code
function isPropertyAccessExpression(
      node: TSESTree.Node,
    ): node is TSESTree.MemberExpression {
      return node.type === AST_NODE_TYPES.MemberExpression && !node.computed;
    }
  • Parameters:
  • node: TSESTree.Node
  • Return Type: node is TSESTree.MemberExpression

isEntityNameExpression(node: TSESTree.Node): node is TSESTree.Identifier | TSESTree.MemberExpression

Code
function isEntityNameExpression(
      node: TSESTree.Node,
    ): node is TSESTree.Identifier | TSESTree.MemberExpression {
      return (
        node.type === AST_NODE_TYPES.Identifier ||
        (isPropertyAccessExpression(node) &&
          isEntityNameExpression(node.object))
      );
    }
  • Parameters:
  • node: TSESTree.Node
  • Return Type: node is TSESTree.Identifier | TSESTree.MemberExpression
  • Calls:
  • isPropertyAccessExpression
  • isEntityNameExpression