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

如何在typescript-eslint自定义规则中验证调用对象为指定类型MyType?

问题:TypeScript-ESLint自定义规则无法验证调用者对象为指定类型

尝试编写TypeScript-ESLint自定义规则,要求确保CallExpression节点的被调用者对象是指定类型MyType,但当前代码中symbol或escapedName常为undefined,无法匹配到MyType。

相关源码:

interface MyType {
   ...
}

const someVariable: MyType = ...;

当前自定义规则代码:

export const rule = createRule({
  name: 'some-name',
  meta: {
    type: 'problem',
    docs: {
      description: 'description',
      recommended: 'error',
    },
    schema: [],
    messages,
  },
  defaultOptions: [],
  create: (context) => {
    return {
      CallExpression(node: TSESTree.CallExpression) {
        const services = ESLintUtils.getParserServices(context);
        const checker = services.program.getTypeChecker();

        const callee = node.callee;
        const object = callee.type === 'MemberExpression' ? callee.object : null;

        if (object === null) {
          return;
        }

        const type = checker.getTypeAtLocation(services.esTreeNodeToTSNodeMap.get(object));

        const isMyType = (
          object.type === 'Identifier' &&
          (type as any)?.symbol?.escapedName === 'MyType'
        );

        if (isMyType) {
          [... do stuff here]
        }
      },
    };
  },
});

解决方案

直接访问type.symbol.escapedName不可靠,因为类型可能通过别名、推导或泛型生成,无法直接关联到原始接口符号。以下是更可靠的实现方式:

核心思路

  1. 先获取变量的符号(而非类型的符号),确保关联到变量声明。
  2. 查找作用域内的MyType接口符号,拿到目标类型。
  3. 使用TypeScript类型检查器的类型兼容判断API,验证变量类型是否匹配MyType。

修改后的规则代码

import { TSESTree } from '@typescript-eslint/utils';
import { ESLintUtils } from '@typescript-eslint/utils';
import * as ts from 'typescript';

export const rule = ESLintUtils.RuleCreator(() => '')({
  name: 'some-name',
  meta: {
    type: 'problem',
    docs: {
      description: '确保CallExpression的调用者对象为MyType类型',
      recommended: 'error',
    },
    schema: [],
    messages: {
      invalidCaller: '调用者对象必须为MyType类型',
    },
  },
  defaultOptions: [],
  create: (context) => {
    return {
      CallExpression(node: TSESTree.CallExpression) {
        const services = ESLintUtils.getParserServices(context);
        const checker = services.program.getTypeChecker();
        const tsNodeMap = services.esTreeNodeToTSNodeMap;

        // 仅处理成员调用(如 someVariable.method())
        if (node.callee.type !== 'MemberExpression') return;
        const objectTsNode = tsNodeMap.get(node.callee.object);
        if (!objectTsNode) return;

        // 获取变量的符号(关联到变量声明)
        const objectSymbol = checker.getSymbolAtLocation(objectTsNode);
        if (!objectSymbol) return;

        // 获取变量的实际类型
        const objectType = checker.getTypeOfSymbolAtLocation(objectSymbol, objectTsNode);
        if (!objectType) return;

        // 在当前作用域查找MyType接口符号
        const myTypeSymbol = checker.getSymbolsInScope(
          objectTsNode,
          ts.SymbolFlags.Interface
        ).find(sym => sym.escapedName === 'MyType');
        if (!myTypeSymbol) return;

        // 获取MyType的类型定义
        const myType = checker.getTypeOfSymbolAtLocation(
          myTypeSymbol,
          myTypeSymbol.valueDeclaration!
        );
        if (!myType) return;

        // 验证变量类型是否兼容MyType(支持赋值兼容,包括继承、别名场景)
        const isMyType = checker.isTypeAssignableTo(objectType, myType);

        if (isMyType) {
          // 执行你的业务逻辑,比如报错提示
          context.report({
            node,
            messageId: 'invalidCaller',
          });
        }
      },
    };
  },
});

关键说明

  • getSymbolAtLocation:直接获取变量节点对应的符号,相比type.symbol更稳定,能准确关联到变量的声明信息。
  • getSymbolsInScope:在当前作用域内查找MyType接口,确保能定位到目标类型定义。
  • isTypeAssignableTo:通过TypeScript的类型系统判断兼容性,支持类型继承、别名等场景,比直接比较名称更准确。
  • 空值处理:添加多处空值校验,避免因节点或符号不存在导致的运行时错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 00:37:11