Skip to content

⬅️ Back to Table of Contents

📄 no-empty-interface

📊 Analysis Summary

Metric Count
🔧 Functions 2
📦 Imports 5
📑 Type Aliases 2

📚 Table of Contents

🛠️ File Location:

📂 packages/eslint-plugin/src/rules/no-empty-interface.ts

📤 Default Export

export default createRule<Options, MessageIds>({ ... })
Property Value
name 'no-empty-interface'
meta.type 'suggestion'
meta.deprecated.deprecatedSince '8.0.0'
meta.deprecated.replacedBy [ { rule: { name: '@typescript-eslint/no-empty-object-type', url: 'https://typescript-eslint.io/rules/no-empty-object...
meta.deprecated.url 'https://github.com/typescript-eslint/typescript-eslint/pull/8977'
meta.docs.description 'Disallow the declaration of empty interfaces'
meta.fixable 'code'
meta.hasSuggestions true
meta.messages.noEmpty 'An empty interface is equivalent to {}.'
meta.messages.noEmptyWithSuper 'An interface declaring no members is equivalent to its supertype.'
meta.replacedBy ['@typescript-eslint/no-empty-object-type']
meta.schema [ { type: 'object', additionalProperties: false, properties: { allowSingleExtends: { type: 'boolean', description: 'W...
defaultOptions [ { allowSingleExtends: false, }, ]

Entry point: create — documented under Functions.


📦 Imports

Name Source
TSESLint @typescript-eslint/utils
ScopeType @typescript-eslint/scope-manager
AST_NODE_TYPES @typescript-eslint/utils
createRule ../util
isDefinitionFile ../util

Functions

create(context: any, [{ allowSingleExtends }]: any): { TSInterfaceDeclaration(node: any): void; }

Parameters:

  • context any
  • [{ allowSingleExtends }] any

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

Calls:

  • context.report
  • context.sourceCode.getText
  • fixer.replaceText
  • context.sourceCode.getScope
  • scope.set .get(node.id.name) ?.defs.some
  • isDefinitionFile (from ../util)

Internal Comments:

// interface contains members --> Nothing to report
// interface extends exactly 1 interface --> Report depending on rule setting

Code
create(context, [{ allowSingleExtends }]) {
    return {
      TSInterfaceDeclaration(node): void {
        if (node.body.body.length !== 0) {
          // interface contains members --> Nothing to report
          return;
        }

        const extend = node.extends;
        if (extend.length === 0) {
          context.report({
            node: node.id,
            messageId: 'noEmpty',
          });
        } else if (
          extend.length === 1 &&
          // interface extends exactly 1 interface --> Report depending on rule setting
          !allowSingleExtends
        ) {
          const fix = (fixer: TSESLint.RuleFixer): TSESLint.RuleFix => {
            let typeParam = '';
            if (node.typeParameters) {
              typeParam = context.sourceCode.getText(node.typeParameters);
            }
            return fixer.replaceText(
              node,
              `type ${context.sourceCode.getText(
                node.id,
              )}${typeParam} = ${context.sourceCode.getText(extend[0])}`,
            );
          };
          const scope = context.sourceCode.getScope(node);

          const mergedWithClassDeclaration = scope.set
            .get(node.id.name)
            ?.defs.some(
              def => def.node.type === AST_NODE_TYPES.ClassDeclaration,
            );

          const isInAmbientDeclaration =
            isDefinitionFile(context.filename) &&
            scope.type === ScopeType.tsModule &&
            scope.block.declare;

          const useAutoFix = !(
            isInAmbientDeclaration || mergedWithClassDeclaration
          );

          context.report({
            node: node.id,
            messageId: 'noEmptyWithSuper',
            ...(useAutoFix
              ? { fix }
              : !mergedWithClassDeclaration
                ? {
                    suggest: [
                      {
                        messageId: 'noEmptyWithSuper',
                        fix,
                      },
                    ],
                  }
                : null),
          });
        }
      },
    };
  }

Internal helpers

Declared inside another function in this file.

fix(fixer: TSESLint.RuleFixer): TSESLint.RuleFix

Parameters:

  • fixer TSESLint.RuleFixer

Returns: TSESLint.RuleFix

Calls:

  • context.sourceCode.getText
  • fixer.replaceText
Code
(fixer: TSESLint.RuleFixer): TSESLint.RuleFix => {
            let typeParam = '';
            if (node.typeParameters) {
              typeParam = context.sourceCode.getText(node.typeParameters);
            }
            return fixer.replaceText(
              node,
              `type ${context.sourceCode.getText(
                node.id,
              )}${typeParam} = ${context.sourceCode.getText(extend[0])}`,
            );
          }

Type Aliases

Options

type Options = [
  {
    allowSingleExtends?: boolean;
  },
];

MessageIds

type MessageIds = 'noEmpty' | 'noEmptyWithSuper';

Generated by Syntax Scribe