ESLint 已移除规则剖析:space-unary-word-ops 及其继任者 space-unary-ops 迁移指南
【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint
导读
space-unary-word-ops是 ESLint 早期版本中用于强制"一元单词运算符(unary word operator)后必须跟空格"的排版类规则,但它在 ESLint v0.10.0 中即被移除,由功能更完整的 space-unary-ops 规则取代。本文以 space-unary-word-ops 官方文档 为骨架,完整还原该规则的检查目标、正误示例,并深入讲解继任规则的三个配置项(words、nonwords、overrides)与底层实现,帮助你理解为何typeof!a属于风格错误、void 0才是推荐写法,以及如何完成从旧规则到新规则的平滑迁移。
规则背景:为什么它被移除
space-unary-word-ops的历史定位非常明确:强制一元单词运算符之后必须有一个空格。所谓"一元单词运算符",指的是用英文单词表示的运算符,包括:
new(构造实例)delete(删除属性)typeof(类型判断)void(求值但不返回结果)
规则的原始描述为 "Requires spaces after unary word operators",即只关心"单词运算符之后"这一侧的空格,覆盖面较窄。由于后来出现的space-unary-ops规则在功能上完全覆盖并超越它(同时管理单词运算符与符号运算符、支持words/nonwords/overrides三个选项),ESLint 官方在v0.10.0将其移除,并在文档中以:::important提示块明确标注:
This rule was removed in ESLint v0.10.0 and replaced by the space-unary-ops rule.
仓库中的迁移数据同样印证了这一替代关系:在 conf/replacements.json 中,space-unary-word-ops被映射为["space-unary-ops"];在 conf/rule-type-list.json 的已移除规则列表中,space-unary-word-ops的replacedBy同样指向space-unary-ops。这意味着 ESLint 在检测到旧规则名时会自动给出"使用新规则名"的迁移提示,用户无需手动记忆映射关系。
原始规则的检查目标
space-unary-word-ops的核心诉求是消除"运算符与操作数之间没有空格"的可读性隐患。当单词运算符与紧跟其后的表达式粘连在一起时,代码很难一眼读懂,甚至可能产生歧义。
不符合规则(incorrect)的代码
以下四组示例展示了规则报错的情形:
typeof!atypeof与!a之间没有空格,语义上等价于typeof (!a),但视觉上极易与"typeof 作用于!"混淆。
void{a:0}void与对象字面量{a:0}粘连,正确写法应为void {a:0}。
new[a][0]new与数组下标表达式粘连,正确写法应为new [a][0]。
delete(a.b)delete与括号表达式粘连。注意这里的情况比较特殊:delete (a.b)才是可接受的写法,而完全不加空格直接写成delete(a.b)会被视为风格错误。
符合规则(correct)的代码
delete a.bdelete后跟空格。值得注意的是,这种写法中空格在语法上是强制要求的(delete与成员表达式之间不能省略空格),因此它天然符合规则。
new Cnew与构造函数名C之间有空格,符合规则。
void 0void后跟空格,作用于字面量0,这是业界标准的写法。
从这组正误对比可以看出,原始规则本质上只做了一件事:在单词运算符与其操作数之间强制保留一个空格。而这一规则在后续演进中,被space-unary-ops以更细粒度、更可配置的方式继承了下来。
继任规则 space-unary-ops:功能全解析
继任规则 space-unary-ops 文档 将问题域扩展为"一元运算符两侧的空格一致性":
This rule enforces consistency regarding the spaces after
wordsunary operators and after/beforenonwordsunary operators.
它将一元运算符划分为两类:
单词运算符(words):new、delete、typeof、void、yield
// new var joe = new Person(); // delete var obj = { foo: 'bar' }; delete obj.foo; // typeof typeof {} // object // void void 0 // undefined符号运算符(nonwords):-、+、--、++、!、!!
if ([1,2,3].indexOf(1) !== -1) {}; foo = --foo; bar = bar++; baz = !foo; qux = !!baz;一个关键细节是:对于words运算符,规则只在不加空格会导致语法歧义的场景生效。例如delete obj.foo中的空格是语法强制的,规则不介入;而delete(obj.foo)中的空格是可选的(delete (obj.foo)与delete(obj.foo)均可解析),此时规则才会生效。
三个配置选项
规则接收一个对象类型的选项,默认值为{ "words": true, "nonwords": false }:
| 选项 | 类型 | 默认值 | 作用 |
|---|---|---|---|
words | boolean | true | 是否要求new、delete、typeof、void、yield等单词运算符之后有空格 |
nonwords | boolean | false | 是否要求-、+、--、++、!、!!等符号运算符之后/之前有空格 |
overrides | object | {} | 针对单个运算符覆盖上述全局设置,value 为 boolean |
overrides的典型用法如下:
"space-unary-ops": [ 2, { "words": true, "nonwords": false, "overrides": { "new": false, "++": true } } ]在该配置下,全局策略是"单词运算符后要空格、符号运算符不要空格",但通过overrides做了两处例外:new之后禁止空格(覆盖words: true),++前后要求空格(覆盖nonwords: false)。
默认选项下的正误示例
不符合规则(incorrect):
/*eslint space-unary-ops: "error"*/ typeof!foo; void{foo:0}; new[foo][0]; delete(foo.bar); ++ foo; foo --; - foo; + "3";/*eslint space-unary-ops: "error"*/ function *foo() { yield(0) }/*eslint space-unary-ops: "error"*/ async function foo() { await(bar); }注意继任规则还额外覆盖了yield与await两个单词运算符,这是原始space-unary-word-ops所不具备的能力——yield(0)、await(bar)在默认配置下同样会被标记为错误。
符合规则(correct):
/*eslint space-unary-ops: "error"*/ // Word unary operator "typeof" is followed by a whitespace. typeof !foo; // Word unary operator "void" is followed by a whitespace. void {foo:0}; // Word unary operator "new" is followed by a whitespace. new [foo][0]; // Word unary operator "delete" is followed by a whitespace. delete (foo.bar); // Unary operator "++" is not followed by whitespace. ++foo; // Unary operator "--" is not preceded by whitespace. foo--; // Unary operator "-" is not followed by whitespace. -foo; // Unary operator "+" is not followed by whitespace. +"3";/*eslint space-unary-ops: "error"*/ function *foo() { yield (0) }/*eslint space-unary-ops: "error"*/ async function foo() { await (bar); }源码级原理:space-unary-ops 是如何实现的
从源码结构看,继任规则实现在 lib/rules/space-unary-ops.js,其meta元数据与检查逻辑共同支撑了上述行为:
- 规则类型与可修复性:
type: "layout",并声明fixable: "whitespace",即所有违规都可以通过--fix自动修复(在单词后插入空格,或删除多余空格)。修复器通过fixer.insertTextAfter/fixer.removeRange对 token 之间的空白区域做精确操作。 - 选项默认值:源码中
const options = context.options[0] || { words: true, nonwords: false };与文档声明的默认值完全一致。 - 单词运算符检查逻辑:
checkUnaryWordOperatorForSpaces会根据是否存在overrides覆盖项决定调用verifyWordHasSpaces(要求有空格)还是verifyWordDoesntHaveSpaces(要求无空格);当没有覆盖项时,退回到options.words的全局设置。 - 符号运算符检查逻辑:
checkForSpaces会区分前缀/后缀场景(如++foo与foo++),对NewExpression还会额外判断firstToken.type === "Keyword",将new当作单词运算符处理。 - 特殊边界情况:
isFirstBangInBangBangExpression专门识别!!双重取反表达式——当nonwords: true时,第一个!与第二个!之间不要求空格,避免误伤!!baz这类常见写法。 - 监听节点范围:规则在
UnaryExpression、UpdateExpression、NewExpression、YieldExpression、AwaitExpression五类 AST 节点上触发检查,覆盖了文档中列出的全部运算符场景。
此外,从 lib/rules/space-unary-ops.js 的meta.deprecated信息可以看到,space-unary-ops自身在 ESLint v8.53.0 起也已标记为废弃(格式化类规则逐步移出 ESLint 核心,由@stylistic/eslint-plugin维护),availableUntil: "11.0.0"。这意味着如果你在使用较新的 ESLint 版本,应优先考虑迁移到 ESLint Stylistic 插件中的同名规则,以保证后续版本升级不受影响。
迁移指南:从旧配置到新配置
若你的项目配置文件中仍在使用space-unary-word-ops,请按以下步骤迁移:
- 替换规则名:将
"space-unary-word-ops": 2改为"space-unary-ops": "error"(或2),替换关系见 conf/replacements.json。 - 理解行为差异:旧规则只强制"单词运算符后有空格",等价于新规则默认配置中的
"words": true部分;新规则还额外检查符号运算符与yield/await,默认"nonwords": false表示符号运算符两侧不允许空格(如++foo、foo--、-foo),与旧规则并不冲突。 - 按需补充选项:如果你需要保留旧规则"只关注单词运算符"的宽松度,可以显式配置
{ "words": true, "nonwords": false };如需更细粒度控制,使用overrides逐运算符覆盖。 - 利用自动修复:由于新规则
fixable: "whitespace",运行eslint . --fix即可自动规范化一元运算符周围的空白,无需手工逐一修改。 - 关注二次迁移:若 ESLint 版本 ≥ v8.53.0,请同时留意规则废弃告警,将
space-unary-ops迁移至@stylistic/eslint-plugin的对应规则,以匹配 ESLint 核心"格式化规则外迁"的整体演进方向。
总结
space-unary-word-ops作为 ESLint 早期的一元运算符空格规则,确立了"typeof、void、new、delete等单词运算符后必须有空格"的代码风格基线,并在 v0.10.0 被功能更全面的space-unary-ops取代。理解这段演进历史,有助于你读懂旧项目的配置遗产,也能更准确地使用words、nonwords、overrides三个选项约束一元运算符的排版,最终写出可读性更强、风格一致的 JavaScript 代码。
【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考