You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何仅对联合类型或枚举类型的switch语句应用ESLint规则?

问题

已实现一个自定义ESLint规则,要求所有switch语句必须包含返回assertUnreachable调用结果的default分支,且传入参数需与switch的变量匹配。但当前规则会作用于所有switch语句,无类型限制。需要调整规则,仅对判别式为联合类型或枚举类型的switch语句触发检查。

已安装typescript-eslint以支持ESLint中的类型检查,现有规则代码如下:

// eslint-disable-next-line no-undef
module.exports = {
  rules: {'exhaustive-switch-on-unions-and-enums': {
    create(context) {
      function getTypeName(node) {
        if (node.type === 'TSTypeReference') {
          return node.typeName.name;
        } else if (node.type === 'TSUnionType') {
          return node.types.map(getTypeName).join(' | ');
        } else if (node.type === 'TSEnumDeclaration') {
          return 'enum';
        }
        return null;
      }
  
      function hasDefaultCaseWithAssertUnreachable(node) {
        if (!node.cases || node.cases.length === 0) {
          return false;
        }
  
        const defaultCase = node.cases.find((caseClause) => caseClause.test === null);
        if (!defaultCase) {
          return false;
        }
  
        const { consequent } = defaultCase;
        if (
          consequent.length === 1 &&
          consequent[0].type === 'ReturnStatement' &&
          consequent[0].argument &&
          consequent[0].argument.type === 'CallExpression' &&
          consequent[0].argument.callee.name === 'assertUnreachable'
        ) {
          const switchType = context.getTypeChecker().getTypeAtLocation(context.getNodeFromContext(node.discriminant));
          const switchTypeName = getTypeName(switchType);
          const argumentType = consequent[0].argument.arguments[0];
          if (argumentType.type === 'Identifier' && argumentType.name === switchTypeName) {
            return true;
          }
        }
  
        return false;
      }
  
      return {
        SwitchStatement(node) {
          if (!hasDefaultCaseWithAssertUnreachable(node)) {
            context.report({
              node,
              message: 'Switch statement must have a default case returning assertUnreachable with the correct type.',
            });
          }
        },
      };
    },
  }}}
解决方案

核心修改点

  • 仅针对联合/枚举类型触发规则:新增类型判断逻辑,过滤非目标类型的switch语句
  • 修正参数匹配逻辑:原代码错误匹配类型名,改为匹配switch变量名

修改后的完整代码

// eslint-disable-next-line no-undef
const ts = require('typescript');

module.exports = {
  rules: {'exhaustive-switch-on-unions-and-enums': {
    create(context) {
      const parserServices = context.parserServices;
      const typeChecker = parserServices.program.getTypeChecker();

      // 判断类型是否为联合类型或枚举类型
      function isUnionOrEnumType(type) {
        // 识别联合类型
        if (type.isUnion()) {
          return true;
        }
        // 通过TypeFlags识别枚举类型
        return !!(type.flags & ts.TypeFlags.Enum);
      }

      function hasValidDefaultCase(node) {
        if (!node.cases || node.cases.length === 0) {
          return false;
        }

        // 查找default分支
        const defaultCase = node.cases.find(caseClause => caseClause.test === null);
        if (!defaultCase) {
          return false;
        }

        const { consequent } = defaultCase;
        // 检查default分支是否为返回assertUnreachable的语句
        if (
          consequent.length === 1 &&
          consequent[0].type === 'ReturnStatement' &&
          consequent[0].argument &&
          consequent[0].argument.type === 'CallExpression' &&
          consequent[0].argument.callee.name === 'assertUnreachable'
        ) {
          const switchVarName = node.discriminant.name;
          // 检查assertUnreachable的参数是否为switch变量
          const arg = consequent[0].argument.arguments[0];
          return arg.type === 'Identifier' && arg.name === switchVarName;
        }

        return false;
      }

      return {
        SwitchStatement(node) {
          const switchDiscriminant = node.discriminant;
          if (!switchDiscriminant) return;

          // 将ESLint AST节点转换为TypeScript AST节点,用于类型检查
          const tsNode = parserServices.esTreeNodeToTSNodeMap.get(switchDiscriminant);
          const type = typeChecker.getTypeAtLocation(tsNode);

          // 仅对联合/枚举类型的判别式触发规则检查
          if (isUnionOrEnumType(type)) {
            if (!hasValidDefaultCase(node)) {
              context.report({
                node,
                message: 'Switch statement on union/enum type must have a default case returning assertUnreachable with the switch variable.',
              });
            }
          }
        },
      };
    },
  }}}

关键说明

  • 借助parserServices.esTreeNodeToTSNodeMap完成ESLint AST到TypeScript AST的转换,确保类型检查器能正确获取判别式的类型信息
  • isUnionOrEnumType通过TypeScript内置的isUnion()方法和TypeFlags枚举,精准识别联合类型与枚举类型
  • hasValidDefaultCase修正了参数匹配逻辑,直接对比assertUnreachable的参数名与switch变量名,符合需求

内容的提问来源于stack exchange,提问作者user2956284

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 10:27:06