必须从ast角度理解eslint规则,因为eslint基于解析后的ast(而非字符串匹配)工作,规则需监听特定节点类型(如variabledeclarator),不理解ast易导致漏检、误报或性能问题。

为什么必须从 AST 角度理解 ESLint 规则
ESLint 不是靠字符串匹配或正则去“猜”代码问题,它真正工作时操作的是 SourceCode 对象——也就是解析后的 AST。这意味着:你写的每条规则,本质是在监听特定 AST 节点类型(如 CallExpression、VariableDeclarator),并在对应节点出现时做逻辑判断。不理解 AST 结构,就容易写出漏检、误报或性能差的规则。
常见错误现象包括:规则只在顶层生效、嵌套函数里失效、箭头函数参数识别失败。这些问题往往不是配置错,而是没意识到 context.getScope() 的作用域层级,或没用 node.parent 向上追溯上下文。
- 调试 AST 结构最直接的方式是运行
eslint --print-config path/to/file.js配合在线工具如 AST Explorer 查看目标代码的真实节点形态 - 所有自定义规则必须在
create(context)返回的对象中注册监听器,键名必须与 ESTree 规范中的节点类型完全一致(如不能写CallExpressionNode,只能写CallExpression) - 避免在监听器里做深度递归遍历——ESLint 已经帮你做了整棵树的遍历,重复遍历会显著拖慢检查速度
如何编写一个禁止「非空对象字面量直接赋值给 let 变量」的规则
这个需求很典型:团队约定 let 只用于后续会重新赋值的变量,而初始化即确定的结构应使用 const。但 const 允许对象内容变更,所以重点是识别“初始化即为完整对象字面量”的场景。
关键在于区分两种 AST 模式:ObjectExpression(对象字面量)是否作为 VariableDeclarator 的 init 值,且其父级声明是 let。
- 不要只检查
node.init.type === "ObjectExpression",还要确认node.parent.kind === "let" - 需排除解构赋值场景(如
let { a } = obj),此时node.init是Identifier,不是ObjectExpression - 要跳过带计算属性或方法的对象(如
{ [key]: 1, fn(){} }),它们更可能被后续修改,可放宽限制
示例核心逻辑:
module.exports = {
meta: { type: "problem", fixable: "code" },
create(context) {
return {
VariableDeclarator(node) {
const init = node.init;
if (
node.parent.kind === "let" &&
init?.type === "ObjectExpression" &&
init.properties.length > 0 &&
!init.properties.some(p => p.computed || p.method)
) {
context.report({
node,
message: "let 声明不应初始化为静态对象字面量,请改用 const",
fix(fixer) {
return fixer.replaceText(node.parent, `const${node.parent.source.slice(3)}`);
}
});
}
}
};
}
};
自定义规则上线前必须验证的三件事
规则写完不等于能进生产。很多团队把规则加进 .eslintrc.js 就直接跑 CI,结果要么大面积报错阻塞提交,要么根本不起作用。
- 用真实项目代码片段测试边界情况:空对象
{}、含注释的对象、带 getter 的对象、TS 接口实现类等 - 确认
parserOptions.ecmaVersion和parser配置与规则兼容——比如用了@typescript-eslint/parser,就不能依赖原生espree的节点类型命名 - 在
package.json的lintscript 中加--debug参数,查看该规则是否被加载、是否命中节点、是否有报错堆栈
AST 规则和 Prettier 的协作边界在哪
很多人混淆 ESLint 和 Prettier 的职责。Prettier 只处理格式(缩进、引号、换行),它不碰 AST 语义;ESLint 基于 AST 做语义检查(比如“是否用了 console.log”、“是否变量未使用”)。两者叠加时,最容易出问题的是修复冲突。
例如:你写了条规则自动把 let 改成 const,但 Prettier 又在下一轮把 const 格式化成单行,而你的规则又因格式变化再次触发……循环发生。
- 所有 ESLint 自动修复(
eslint --fix)应在 Prettier 执行前完成;推荐顺序是:eslint --fix→prettier --write - 禁用 Prettier 管控的 ESLint 格式类规则(如
indent、quotes),统一交给 Prettier,避免 AST 层和格式层互相干扰 - 如果规则涉及重写代码结构(如替换整个表达式),务必在
meta.fixable = "code"中声明,并确保修复后仍能被 Prettier 安全格式化
最常被忽略的一点:AST 规则无法检测运行时行为,比如某个函数调用是否真会修改全局状态。这类问题需要结合单元测试或 TypeScript 类型约束,不能指望 ESLint 单独解决。











