如何在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不可靠,因为类型可能通过别名、推导或泛型生成,无法直接关联到原始接口符号。以下是更可靠的实现方式:
核心思路
- 先获取变量的符号(而非类型的符号),确保关联到变量声明。
- 查找作用域内的
MyType接口符号,拿到目标类型。 - 使用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
相关产品推荐
相关产品推荐

