Rome Lint 规则 noDoubleEquals:强制使用 === 与 !== 的 Lint 规则及自动修复实现
2026/9/20 17:03:26 网站建设 项目流程

Rome Lint 规则 noDoubleEquals:强制使用 === 与 !== 的 Lint 规则及自动修复实现

【免费下载链接】toolsUnified developer tools for JavaScript, TypeScript, and the web项目地址: https://gitcode.com/gh_mirrors/to/tools

本文以 Rome 官方文档中的noDoubleEquals规则说明为主体,系统讲解该规则的设计动机、检测语义、== null例外、诊断输出格式与 Quick Fix 自动修复机制,并结合 Rome 仓库中该规则的真实源码与测试用例,说明它如何在JsBinaryExpression上触发诊断、如何生成修复补丁,以及如何在 rome.json 中配置或在代码中抑制该规则。

规则定位:v0.7.0 引入的推荐规则

noDoubleEquals是 Rome Linter 中suspicious(可疑代码)分组下的一条规则,自v0.7.0引入,并被 Rome 标记为推荐规则(recommended)。规则文档见 noDoubleEquals.md,规则源码位于 no_double_equals.rs。

文档给出的核心要求是:

Require the use of===and!==(要求使用===!==

设计动机在于:用==代替===进行比较通常是不好的实践。双等号运算符会触发隐式的类型强制转换(type coercion)——例如"1" == 1true[] == falsetrue——这类行为依赖语言规范中繁琐的抽象相等比较算法,容易写出难以推理的代码。因此使用严格相等运算符===/!==几乎总是最佳实践。

从 declare_rule 宏声明可以确认规则的元信息:

pub(crate) NoDoubleEquals { version: "0.7.0", name: "noDoubleEquals", recommended: true, }

该规则被注册进suspicious分组(见 suspicious.rs 中的self::no_double_equals::NoDoubleEquals),因此它的完整诊断 ID 为lint/suspicious/noDoubleEquals

检测语义:哪些==/!=会被报告

规则的检测范围是二元表达式(JsBinaryExpression):当运算符是==!=,且比较的两侧都不是null字面量时,就会报告诊断。

文档给出的示例与源码行为完全一致:

无效示例(会被报告)

foo == bar

运行rome lint后输出:

suspicious/noDoubleEquals.js:1:5 lint/suspicious/noDoubleEquals FIXABLE ! Use === instead of == > 1 │ foo == bar │ ^^ i == is only allowed when comparing against null i Using === may be unsafe if you are relying on type coercion i Suggested fix: Use === 1 │ foo·==·bar │ +

诊断包含四层信息:主消息(Use===instead of==)、detail(==只允许用于与null比较)、note(如果依赖类型转换,改用===可能不安全)以及建议修复。

有效示例(例外:与null比较)

出于易用性考虑,该规则对== null做了专门豁免——因为x == null同时匹配nullundefined,是 JavaScript 社区公认的空值检查惯用法。以下写法均合法:

foo == null foo != null null == foo null != foo

这一豁免在源码中体现为 run 函数对左右操作数的检查:

fn run(ctx: &RuleContext<Self>) -> Option<Self::State> { let n = ctx.query(); let op = n.operator_token().ok()?; // 只关心 == 和 != 两种运算符 if !matches!(op.kind(), EQ2 | NEQ) { return None; } // 左侧或右侧是 null 字面量则放行 if is_null_literal(n.left()) || is_null_literal(n.right()) { return None; } Some(op) }

其中 is_null_literal 精确匹配 AST 中的JsNullLiteralExpression,所以只有null这一个字面量触发豁免,undefined或其他常量不在此列。同时注意运算符判断同时覆盖了==EQ2)和!=NEQ),即foo != bar同样会被要求写成foo !== bar

另外,该规则没有可配置选项:规则实现中type Options = ();(见 no_double_equals.rs 中type Options = ();一处),意味着它只有开或关两种状态,行为完全由上述语义决定。

诊断的生成细节

diagnostic方法根据运算符种类动态生成提示文案(==提示改用===!=提示改用!==),并将诊断锚定在运算符 token 的精确范围上(op.text_trimmed_range()),这就是输出中^^只下划线覆盖两个字符的原因。完整实现见 diagnostic 函数:

fn diagnostic(_ctx: &RuleContext<Self>, op: &Self::State) -> Option<RuleDiagnostic> { let text_trimmed = op.text_trimmed(); let suggestion = if op.kind() == EQ2 { "===" } else { "!==" }; // ... Some(RuleDiagnostic::new( rule_category!(), op.text_trimmed_range(), markup! { "Use "<Emphasis>{suggestion}</Emphasis>" instead of "<Emphasis>{text_trimmed}</Emphasis> }, ) .detail(...) // "== is only allowed when comparing against null" .note(...) // "Using === may be unsafe if you are relying on type coercion" .description(description)) }

Quick Fix:单字符替换式自动修复

诊断头部的FIXABLE标记来自规则的action方法。它通过 Rome 的语法树批量变异(BatchMutation)把运算符 token原地替换为三字符运算符:

fn action(ctx: &RuleContext<Self>, op: &Self::State) -> Option<JsRuleAction> { let mut mutation = ctx.root().begin(); let suggestion = if op.kind() == EQ2 { T![===] } else { T![!==] }; mutation.replace_token(op.clone(), make::token(suggestion)); Some(JsRuleAction { category: ActionCategory::QuickFix, applicability: Applicability::MaybeIncorrect, message: markup! { "Use "<Emphasis>{suggestion.to_string().unwrap()}</Emphasis> }.to_owned(), mutation, }) }

见 action 函数。两个实现细节值得注意:

  1. 修复方式极简:不是重写表达式,而是把==token 换成===token,只增加一个字符,因此在模板字符串、if条件等任何上下文都安全;
  2. 适用性级别为MaybeIncorrect:因为诊断中已经提示“如果代码依赖类型转换,改用===可能不安全”,所以自动修复被标记为“可能不正确”,由用户自行决定是否应用,而非无条件安全的修复。

测试用例对检测边界的覆盖

仓库中的规格测试 invalid.js 覆盖了 5 种典型场景,对应快照 invalid.js.snap:

const foo = ` text ${a == b} // 模板字符串插值中的 == `; // existing comment a == b; // 独立表达式语句 if (a == b) { // if 条件中的 == false; } if (/** some weird comment **/ a == b) { // 操作符前有注释的边界情况 } let a = `Output of "rome rage": formatter enabled: ${formatter == true} // 与布尔字面量比较 linter: ${linter} `;

5 处==全部被报告为FIXABLE诊断,包括注释紧邻运算符、模板字符串内部等容易影响列号计算的位置。有效用例 valid.jsonc 则确认了a == nulla != null不产生任何诊断。此外,JSONC 用例 invalid.jsonc 还专门覆盖了a == 0/a != 0这类容易被误判为“空值检查”的场景——它们不属于null豁免,会被正常报告。

实战使用:检查、修复与抑制

运行规则

noDoubleEquals是推荐规则,默认随rome lint/rome check生效,无需在配置中显式开启:

npx rome lint ./your_project

命中违规时,编辑器(如 VS Code 扩展或 LSP)与 CLI 均会展示上文所述的诊断,并可通过 Quick Fix 一键应用===替换。

关闭规则或逐行豁免

规则文档末尾的两个相关链接(Disable a rule 与 Rule options)指向 Linter 配置章节,说明了两条标准路径:

  1. 配置级关闭:在项目根目录的rome.json中,通过linter.rules将该规则设为"off",即可对整个项目禁用noDoubleEquals
  2. 行级抑制:对确实依赖类型转换的个别比较,可以插入rome-ignore注释抑制单条诊断。抑制功能有专门的测试验证,见 noDoubleEquals.js.snap,其中展示了自动修复会把代码改写为带抑制注释的形式:
// rome-ignore lint/suspicious/noDoubleEquals: <explanation> a == b;

注释后必须跟一句解释(<explanation>),这是 Rome 抑制注释的约定格式。

小结

noDoubleEquals虽然只针对两个运算符,但它完整展示了 Rome 一条 Lint 规则的全貌:declare_rule!声明元信息(版本、名称、推荐级别)→runJsBinaryExpressionAST 上精确匹配==/!=并豁免null比较 →diagnostic锚定运算符范围生成多层级提示 →action以单 token 替换提供MaybeIncorrect级别的 Quick Fix。理解这条规则的实现路径(no_double_equals.rs)后,再阅读suspicious分组下的其他规则时,模式是相通的。

【免费下载链接】toolsUnified developer tools for JavaScript, TypeScript, and the web项目地址: https://gitcode.com/gh_mirrors/to/tools

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询