ESLint 的 object-property-newline 规则:强制对象字面量属性换行的完整实战指南
【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint
object-property-newline是 ESLint 核心内置的一条 layout(布局)类规则,用于约束对象字面量中属性声明的排列位置:它默认禁止"任一属性的任何部分与另一属性的任何部分出现在同一行",也允许通过对象选项放行"所有属性完整地位于同一行"的特例。本文以 docs/src/rules/object-property-newline.md 为主线,结合 lib/rules/object-property-newline.js 的源码实现与 tests/lib/rules/object-property-newline.js 的测试用例,系统讲解该规则的动机、选项、边界行为、--fix修复机制、迁移建议及配套规则,帮助你理解并落地这一排版约束。
规则概览:它能做什么
该规则允许你限制对象字面量中属性声明(property specification)的排列位置。你可以绝对禁止任何属性声明的任一部分与另一属性声明的任一部分出现在同一行;也可以传入一个对象选项,允许例外:只要对象字面量的所有属性声明完整地位于同一行(即整段属性声明只有一行),就予以放行。
一句话概括判定逻辑:默认情况下"一行只能放一个属性";开启例外后"要么所有属性都在同一行,要么每个属性各自成行",不允许"部分属性同行、部分属性分行"的中间形态。
规则配置方式
该规则属于 layout 类型,recommended为false,不会随 ESLint 推荐配置启用,需要显式声明。在 flat config(eslint.config.js)中可这样启用:
export default [ { rules: { "object-property-newline": ["error", { "allowAllPropertiesOnSameLine": true }] } } ];在旧的 eslintrc 风格配置中写法为:
{ "rules": { "object-property-newline": ["error", { "allowAllPropertiesOnSameLine": false }] } }命令行直接指定:
npx eslint --rule 'object-property-newline: error' src/从 lib/rules/object-property-newline.js 的 schema 定义可以看到,该规则只接受一个对象选项,包含两个布尔字段:
| 选项字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
allowAllPropertiesOnSameLine | boolean | false | 为true时,允许所有属性声明完整地位于同一行 |
allowMultiplePropertiesPerLine | boolean | false | 已弃用的旧名,语义与上一项完全相同,仅为向后兼容保留 |
additionalProperties: false意味着传入 schema 之外的任何字段都会触发配置校验错误。此外规则注册为fixable: "whitespace",表明其自动修复仅涉及空白字符层面的改动。
规则内部定义了两条消息(lib/rules/object-property-newline.js):
propertiesOnNewlineAll:"Object properties must go on a new line if they aren't all on the same line."(开启了allowAllPropertiesOnSameLine时使用)propertiesOnNewline:"Object properties must go on a new line."(默认模式使用)
设计动机
提升可读性:一属性一行
许多代码风格指南要求属性声明各占一行,以获得更好的可读性。默认配置下,以下所有写法都会被判为违规:
const newObject = {a: 1, b: [2, {a: 3, b: 4}]}; const newObject = { a: 1, b: [2, {a: 3, b: 4}] }; const newObject = { a: 1, b: [2, {a: 3, b: 4}] }; const newObject = { a: 1, b: [ 2, {a: 3, b: 4} ] };合规的写法是让每一级对象的所有属性各自成行:
const newObject = { a: 1, b: [2, { a: 3, b: 4 }] };或
const newObject = { a: 1, b: [ 2, { a: 3, b: 4 } ] };注意:嵌套对象同样受规则约束——示例中内层{a: 3, b: 4}的两个属性也必须分行。
提升 diff 的精确度
属性一属性一行的另一个实际收益是:修改单个属性时,git diff 只涉及被改的那一行,代码评审更聚焦:
// 更精确的 diff var obj = { foo: "foo", - bar: "bar", + bar: "bazz", baz: "baz" };// 不精确的 diff:整行被重写 -var obj = { foo: "foo", bar: "bar", baz: "baz" }; +var obj = { foo: "foo", bar: "bazz", baz: "baz" };第二种写法下,任何一处属性值的改动都会让 review 者看到整行属性列表的变动,难以快速定位真实变化点。
可选项:allowAllPropertiesOnSameLine
规则提供了唯一对象选项allowAllPropertiesOnSameLine(已弃用的同义词为allowMultiplePropertiesPerLine)。设置为true后,所有属性声明完整位于同一行的对象字面量(例如上面示例中的前两种)将被放行,但下面这种写法依然违规:
const newObject = { a: 'a.m.', b: 'p.m.', c: 'daylight saving time' };原因是:只有两个属性(而非全部属性)位于同一行。也就是说,开启该选项后的判定逻辑是——要么所有属性都在一行(放行),要么从第一个属性的第一个字符到最后一个属性的最后一个字符之间只要发生了换行,就必须做到每个属性各自独占一行。
这一点与源码实现完全吻合。lib/rules/object-property-newline.js 中,当allowSameLine为真时,规则先取第一个属性的首个 token 与最后一个属性的末个 token,若两者位于同一行则直接放行;否则继续走逐行检查逻辑。
对各类属性写法的覆盖
该规则对所有属性声明一视同仁,无论使用哪种写法:
a: 1(ES5 键值对)a(ES2015 简写属性 shorthand)[`prop${a}`](ES2015 计算属性名 computed property name)- 也包括方法声明
bar() {}与展开运算符...{}(后者在 tests/lib/rules/object-property-newline.js 中通过ecmaVersion: 2018的场景得到验证)
因此,默认配置下以下写法都会被禁止:
const newObject = { a: 1, [ process.argv[4] ]: '01' }; const newObject = { a: 1, [process.argv[4]]: '01' };计算属性名开头的[被视为该属性声明的一部分(这从 lib/rules/object-property-newline.js 中sourceCode.getFirstToken的取法可以印证:属性声明的首个 token 即键名区域的起点)。这一行为与下文提到的 JSCS 规则不同——JSCS 的对应规则不把计算属性名的前导[视为属性声明的一部分,因而它只禁止上述第二种写法、却放行第一种。
多行属性值:一行只要有"1 个字符"重叠即违规
规则禁止"一个属性声明的至少 1 个字符与另一个属性声明的至少 1 个字符出现在同一行"。例如下面这段代码会被禁止:
const newObject = {a: [ 'Officiële website van de Europese Unie', 'Официален уебсайт на Европейския съюз' ], b: 2};原因是:属性a声明的值部分的收尾]与属性b的声明位于同一行。即便开启了allowAllPropertiesOnSameLine也不能豁免此例——因为整个属性集合横跨了 4 行,而不是 1 行,不满足"所有属性在同一行"的前提。
这一点同样由源码的相邻属性 token 比较逻辑保证:规则逐对比较"上一个属性的最后一个 token"与"当前属性的第一个 token"是否在同一行(lib/rules/object-property-newline.js)。测试用例 tests/lib/rules/object-property-newline.js 也验证了{k1: [\n'foo', 'bar'\n], k2: 'val1'}这种"上一属性值尾部与下一属性同行"的场景会被报告并在逗号后插入换行。
属性间的分隔符(逗号与空白)不算属性的一部分
用于分隔属性声明的逗号及任何空白不被视为属性声明的一部分。因此以下两种格式都合规:
const newFunction = multiplier => ({ a: 2 * multiplier, b: 4 * multiplier, c: 8 * multiplier }); const newFunction = multiplier => ({ a: 2 * multiplier , b: 4 * multiplier , c: 8 * multiplier });第二种"前导逗号"(comma-first)风格也被接受,因为逗号与空白位于行首、不与任何属性声明字符同行。同样地,这与 JSCS 的行为不同——JSCS 放行第一种、却禁止第二种格式。测试用例 tests/lib/rules/object-property-newline.js 中的var obj = {\nk1: 'val1'\n, k2: 'val2'\n, k3: 'val3'\n, k4: 'val4'\n};被列为 valid,印证了这一结论。
--fix 自动修复行为
使用命令行--fix选项时,违反该规则的对象字面量通常会被自动修正。修复方式:只要同一行上还有上一个属性的部分或全部内容,就把当前属性声明移到下一行。例如:
const newObject = { a: 'a.m.', b: 'p.m.', c: 'daylight saving time' };会被转换为:
const newObject = { a: 'a.m.', b: 'p.m.', c: 'daylight saving time' };(注意:修复后的缩进由indent等规则负责,本规则的 fix 只负责插入换行符,不处理缩进。)
关于自动修复,还有几个必须知道的行为边界:
修复不受选项影响:是否执行修复与
allowAllPropertiesOnSameLine是否为true无关。换句话说,即使选项允许"所有属性在一行",ESLint 也绝不会把属性集合合并回单行——修复永远是"拆分"而非"合并"。注释导致无法修复:如果一行中第二个及后续属性声明之前紧跟注释,ESLint 不会自动修复——因为它无法确定该把注释放到哪一行。从源码可见,fix 逻辑取当前属性首个 token 之前的逗号,检查逗号与下一属性之间(lib/rules/object-property-newline.js)的文本,若
trim()后仍有内容(即存在注释),则返回null放弃修复,只报告错误。测试 tests/lib/rules/object-property-newline.js 中({ foo: 1, /* comment */ bar: 2 })的output: null明确标注了"因注释而不修复";而注释位于属性值内部的({ foo: 1 /* comment */, bar: 2 })则可以正常修复。与其他规则协同:如上所示,仅应用本规则的
--fix并不满足indent等排版规则的要求;但如果这些规则同时启用,ESLint 会在同一次修复中一并应用它们,最终结果仍然合规。
从源码看 fix 的实现细节
在 lib/rules/object-property-newline.js 中,规则遍历ObjectExpression的所有属性:
- 对每一对相邻属性,取上一个属性的
getLastToken与当前属性的getFirstToken,比较二者所在行号; - 行号相同即报告,
loc指向当前属性首个 token 的位置; - 修复时用
getTokenBefore找到当前属性前的逗号,将"逗号之后、当前属性之前"的整段文本替换为单个\n(fixer.replaceTextRange(rangeAfterComma, "\n")); - 若该区间存在注释(trim 后非空),返回
null放弃修复。
错误与正确代码示例
错误示例(无选项,或allowAllPropertiesOnSameLine为false)
/*eslint object-property-newline: "error"*/ const obj0 = { foo: "foo", bar: "bar", baz: "baz" }; const obj1 = { foo: "foo", bar: "bar", baz: "baz" }; const obj2 = { foo: "foo", bar: "bar", baz: "baz" }; const obj3 = { [process.argv[3] ? "foo" : "bar"]: 0, baz: [ 1, 2, 4, 8 ] }; const a = "antidisestablishmentarianistically"; const b = "yugoslavyalılaştırabildiklerimizdenmişsiniz"; const obj4 = {a, b}; const domain = process.argv[4]; const obj5 = { foo: "foo", [ domain.includes(":") ? "complexdomain" : "simpledomain" ]: true};注意obj4中简写属性{a, b}同样违规;obj5中计算属性名的[起始行与foo属性同行,也被禁止。
正确示例(无选项,或allowAllPropertiesOnSameLine为false)
/*eslint object-property-newline: "error"*/ const obj1 = { foo: "foo", bar: "bar", baz: "baz" }; const obj2 = { foo: "foo" , bar: "bar" , baz: "baz" }; const user = process.argv[2]; const obj3 = { user, [process.argv[3] ? "foo" : "bar"]: 0, baz: [ 1, 2, 4, 8 ] };obj2展示了前导逗号风格同样合规;obj3展示了简写属性、计算属性名与多行数组值共存时的合规写法。
开启{ "allowAllPropertiesOnSameLine": true }后的额外正确示例
/*eslint object-property-newline: ["error", { "allowAllPropertiesOnSameLine": true }]*/ const obj = { foo: "foo", bar: "bar", baz: "baz" }; const obj2 = { foo: "foo", bar: "bar", baz: "baz" }; const user = process.argv[2]; const obj3 = { user, [process.argv[3] ? "foo" : "bar"]: 0, baz: [1, 2, 4, 8] };开启选项后,只要"所有属性都在同一行",无论是在一行内的独立语句还是跨行对象的某一整行,都视为合规;而"部分同行、部分分行"的写法依然违规。相关场景在 tests/lib/rules/object-property-newline.js 中有成体系的验证。
何时不使用此规则
如果你希望逐案决定属性声明是否分行(而不是由规则强制统一),可以关闭该规则。尤其当团队更偏好紧凑的单行对象(例如测试数据、配置对象),或已使用 Prettier 等格式化工具统一排版时,该规则可以关闭以免与格式化流程冲突。
与其他规则的关系
该规则的 frontmatter 声明了四个关联规则(见 docs/src/rules/object-property-newline.md 头部):
- brace-style:控制代码块的花括号换行风格;
- comma-dangle:控制尾随逗号;
- key-spacing:控制对象键值之间的空格;
- object-curly-spacing:控制对象花括号内部间距。
在自动修复场景中,本规则只负责插入换行,缩进与花括号间距等需要上述规则配合,才能得到最终整洁的排版。
兼容性与 JSCS
该规则与 JSCS 的requireObjectKeysOnNewLine规则部分兼容,主要差异有两处(已在前面相应小节详述):
- 对计算属性名前导
[的归属判定不同:本规则将其视为属性声明的一部分,JSCS 不视为; - 对前导逗号(comma-first)风格的态度不同:本规则放行,JSCS 禁止。
弃用状态与迁移建议
需要特别提醒的是:该规则目前处于弃用状态。从 lib/rules/object-property-newline.js 与 docs/src/_data/rules_meta.json 可以看到其弃用元数据:
- deprecatedSince:
8.53.0(ESLint v8.53.0 起标记弃用) - availableUntil:
11.0.0(计划在 v11.0.0 移除) - 弃用原因:格式化类规则正逐步移出 ESLint 核心(Formatting rules are being moved out of ESLint core)
- 替代方案:ESLint Stylistic 项目负责维护这些已弃用的风格类核心规则,对应规则为
@stylistic/eslint-plugin中的object-property-newline;ESLint 建议按 ESLint Stylistic 的迁移指南 将旧配置迁移到该插件,避免在 v11.0.0 移除后规则失效。
如果你的项目仍在使用 ESLint 核心的该规则,建议尽早规划迁移:安装@stylistic/eslint-plugin并让其中的同名规则接管原有配置,两者在选项语义上保持一致(仍支持allowAllPropertiesOnSameLine),迁移成本主要集中在规则来源的切换。
小结
object-property-newline通过"一行最多一个属性"的约束,显著提升对象字面量的可读性与 diff 精确度;其唯一选项allowAllPropertiesOnSameLine提供了"全放行或全分行"的折中模式,兼顾紧凑单行对象与严格分行两种风格。理解它对计算属性、简写属性、多行值、分隔符与注释的处理边界,并注意其自 v8.53.0 起的弃用状态,可以帮助你在新旧配置体系中正确落地这一排版规范。
【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考