eslint-plugin-unicorn 规则解析:prefer-default-parameters 默认参数优先
2026/9/19 22:18:30 网站建设 项目流程

eslint-plugin-unicorn 规则解析:prefer-default-parameters 默认参数优先

【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn

导读

prefer-default-parameters是 eslint-plugin-unicorn 中一条用于消除函数参数"先接收、再重赋值"反模式的规则。它检测函数体内对参数进行的默认值重赋值(包括=配合||/??,以及||=/??=逻辑赋值),并建议改用 ES6 默认参数语法。读完本文你将掌握:该规则的触发条件与例外场景、它与null/undefined语义差异的底层原理、修复器(suggestion)的工作方式,以及如何在recommended/unopinionated配置中启用它。

规则概览:把"重赋值"改写成"默认参数"

规则定义位于 rules/prefer-default-parameters.js,并在 rules/index.js 中以prefer-default-parameters名称导出。它属于suggestion类型的规则,通过 ESLint 的 editor suggestions 机制提供手动修复能力(hasSuggestions: true),不会自动改写代码,而是给出可一键应用的修复建议。

该规则在以下配置中默认启用:

  • recommended(推荐配置)
  • ☑️unopinionated(无观点配置)

核心规则:只报告字面量默认值的重赋值

匹配的三种模式

规则只报告对字面量值(Literal)的默认值重赋值,具体包括三种语法形态:

// 1. 赋值语句 + || 逻辑或(仅当 foo 为 falsy 时取 'bar') function abc(foo) { foo = foo || 'bar'; // ❌ } // 2. 赋值语句 + ?? 空值合并(仅当 foo 为 null/undefined 时取 'bar') function abc(foo) { foo = foo ?? 'bar'; // ❌ } // 3. 逻辑赋值运算符 ||= 与 ??= function abc(foo) { foo ||= 'bar'; // ❌ } function abc(foo) { foo ??= 'bar'; // ❌ }

从源码看,getDefaultAssignment函数(rules/prefer-default-parameters.js)精确地刻画了这三种形态:

  • =赋值,要求右侧必须是LogicalExpression,且运算符为||??,左操作数是标识符(即参数本身),右操作数是Literal
  • ||=??=,则直接要求右侧是Literal

这意味着foo = foo || bar()foo = foo || {bar}foo = foo && 'bar'foo &&= 'bar'这类"非纯字面量"或"非||/??运算符"的情况都不会被报告——测试用例中这些情形全部列为 valid(见 test/prefer-default-parameters.js)。

关键语义:字面量修复是安全的

为什么只处理字面量?因为foo = foo || 123中的123无副作用的表达式,可以安全地挪进参数默认值。如果默认值是函数调用bar(),移到参数默认值后,其求值时机会改变(每次调用前先求值,而不是在函数体内按需求值),可能导致行为变化,因此规则明确放行:

// ✅ 默认值包含调用,规则不报告 function abc(foo) { foo = foo || bar(); }

测试中同样验证了foo ||= bar()不会被报告(test/prefer-default-parameters.js)。

另一个被支持的重构:新建变量承接默认值

除了直接重赋值参数,规则还支持把"用参数和||/??计算出的新变量"改写成参数默认值:

// ❌ function abc(foo) { const bar = foo || 'bar'; } // ✅ function abc(bar = 'bar') {}

对应测试见 test/prefer-default-parameters.js。此时要求该变量声明必须是函数体内唯一的声明parent.declarations.length === 1),且该参数在默认赋值之前没有被其他代码引用。

为什么这是改进:||与默认参数在 falsy 值上的分歧

文档强调了一个容易踩坑的语义差异:foo = foo || 123foo为任何 falsy 值(0''falseNaNnullundefined)时都会取123,而默认参数只会在传入undefined时生效。

function abc(foo) { foo = foo || 'bar'; } abc(0); // foo 变为 'bar',0 被吞掉 abc(''); // foo 变为 'bar',空字符串被吞掉 function abc(foo = 'bar') {} abc(0); // foo 仍为 0 abc(''); // foo 仍为 ''

因此这条规则实际在推动代码从"宽泛的 falsy 兜底"转向"精确的undefined兜底",避免函数在收到合法 falsy 入参时产生令人困惑的行为。

null的关系:默认参数不处理null

文档明确指出:如果你希望函数对null和其他 falsy 值采取与undefined相同的处理方式,应当禁用本规则。因为默认参数仅对undefined生效,传入null时参数仍为null。项目官方态度是推荐 moving away fromnull(向null告别),认为更清晰的做法是用undefined表达"未传值"。

从源码实现看,??空值合并运算符是唯一能兼顾nullundefined的语法,但它与默认参数的语义仍然不完全等价——这也是为何修复建议只在字面量场景下安全。

规则的"红线":什么情况下拒绝报告

规则在 AST 层面做了大量保守检查,以下情况一律不报告(相应 valid 测试分布在 test/prefer-default-parameters.js):

1. 参数在默认赋值前已被引用

如果参数在赋值语句之前被使用过(如console.log(foo)),改写后引用顺序会变化,规则放弃:

// ❌ 不报告 function abc(foo) { console.log(foo); foo = foo || 123; }

源码中hasExtraReferences(rules/prefer-default-parameters.js)负责检查:对于赋值语句,要求参数的第一个引用就是赋值本身;对于变量声明模式,要求参数在整个函数内只有这一处引用。

2. 赋值语句之前存在可能产生副作用的前置语句

如果默认赋值之前出现任何类调用表达式——函数调用CallExpressionnew表达式、动态import()、标签模板——规则放弃报告,因为默认参数会在这些副作用发生之前就完成求值,可能改变程序行为:

// ❌ 不报告(bar() 的副作用顺序会变) function abc(foo) { bar(); foo = foo || 123; }

containsCallExpression(rules/prefer-default-parameters.js)递归遍历语句中的全部子节点,callLikeExpressionTypes集合(rules/prefer-default-parameters.js)列出了这四类"调用类"节点;hasSideEffects(rules/prefer-default-parameters.js)再限定"在赋值语句之前出现的"才计入。测试专门覆盖了new SideEffects()import('side-effects')sideEffects\template`` 等场景(test/prefer-default-parameters.js)。

3. 目标参数必须是最后一个参数

规则只处理函数的最后一个参数:

// ❌ 不报告(foo 不是最后一个参数) function abc(foo, bar) { foo = foo || 'bar'; }

源码中通过params.at(-1)取最后一个参数并校验类型与名称(rules/prefer-default-parameters.js),注释里明确这是为了避免与default-param-last规则冲突——ESLint 要求默认参数必须排在末尾,如果改写中间参数会违反该规则。Rest 参数(...foo)和已是AssignmentPatternfoo = 'bar')的参数同样被排除。

4. 函数体包含'use strict'指令

'use strict'的函数不会被报告(rules/prefer-default-parameters.js),测试见 test/prefer-default-parameters.js。

5. 参数名冲突与重复声明

如果函数内其他参数与默认值的目标变量同名(例如解构参数{bar}与要新建的bar变量冲突),或存在重复参数名(function abc(foo, foo)),规则放弃。hasParameterNameCollision通过检查变量的定义节点是否为Parameter类型来判断(rules/prefer-default-parameters.js)。

6. 仅位于函数体顶层的语句

getDefaultParameterProblem要求赋值语句的父节点必须是函数体本身(node.parent !== currentFunction.body则跳过),因此嵌套在iffor等块级语句内的重赋值不会被报告:

// ❌ 不报告 function abc(foo) { if (condition) { foo ||= 'bar'; } }

7. 只在"函数上下文"内工作

规则通过functionStack栈(rules/prefer-default-parameters.js)跟踪FunctionDeclarationFunctionExpressionArrowFunctionExpressionfunctionTypes,见 rules/ast/function-types.js)三类函数。模块顶层foo = foo || 'bar'这种非函数语句自然不在检测范围(见 valid 测试 test/prefer-default-parameters.js)。

修复建议(Suggestion)是如何工作的

规则不提供自动修复,而是给出可手动应用的编辑器建议,消息 ID 为preferDefaultParametersSuggest("Replace reassignment with default parameter.")。修复逻辑包含两个步骤(rules/prefer-default-parameters.js):

  1. 改写参数:把原参数名替换为参数名 = 默认值AssignmentPattern),必要时补上括号;
  2. 删除原赋值语句:由fixDefaultExpression(rules/prefer-default-parameters.js)处理,它会智能区分三种布局——赋值语句独占一行时整行删除、行尾带空格时连空格一并删除、否则仅删除节点本身。

括号补充:单参数箭头函数的特例

needsParentheses(rules/prefer-default-parameters.js)处理了单参数无括号箭头函数的括号问题:

// 原代码 const abc = foo => { foo = foo || 'bar'; }; // 修复后必须补括号,否则语法错误 const abc = (foo = 'bar') => { };

多参数函数不需要补括号,因为参数列表本身已带括号(测试见 test/prefer-default-parameters.js)。

带注释时不提供建议

如果赋值语句或参数内含有注释,修复建议会被abort()中止,仅报告错误而不给修复,避免破坏注释:

function abc(foo) { foo ||= /* Keep comment. */ 'bar'; // 报告,但无 suggestions }

对应测试见 test/prefer-default-parameters.js。

TypeScript 支持

规则支持 TypeScript 语法:修复时会保留参数的类型注解(typeAnnotation),把foo?: stringfoo ??= 'bar'修复为foo: string = 'bar'(去掉可选标记、补上默认值),并把const bar: string = foo || 'bar'重构为bar: string = 'bar'。相关测试使用parsers.typescript解析器(test/prefer-default-parameters.js)。若参数类型注解处含注释,则不提供建议。

规则在 AST 层面的触发路径

规则的监听逻辑(rules/prefer-default-parameters.js)只挂载两个事件:

  • AssignmentExpression:仅当它是ExpressionStatement的完整表达式时处理(foo = foo || 123这种语句形态),并将运算符(=||=??=)传给getDefaultAssignment
  • VariableDeclarator:仅当它是单一声明的VariableDeclaration时处理(const bar = foo || 'bar'),运算符固定为=

函数进入/退出时分别 push/pop 到functionStack,确保检测始终落在正确的函数上下文中;最终通过findVariable(来自@eslint-community/eslint-utils)解析参数的引用关系,配合hasExtraReferenceshasSideEffectshasParameterNameCollision三重校验后才产生报告。这也解释了为什么同一段重赋值代码,出现在不同函数或不同位置时,报告行为会截然不同。

实战建议与使用前提

  • 启用方式:使用recommendedunopinionated配置即可自动开启;也可在 flat config 中单独配置'unicorn/prefer-default-parameters': 'error'
  • 什么场景会得到报告:函数最后一个参数、函数体第一条语句、纯字面量默认值、无前置副作用/引用/命名冲突、非'use strict'函数。
  • 什么场景建议关闭:如果团队代码中null是有意义的值(如数据库查询结果、外部 API 数据),且你希望nullundefined触发相同的兜底逻辑,应禁用本规则,因为默认参数无法覆盖null
  • 修复方式:手动应用编辑器的 suggestion,而不是依赖--fix自动修改;规则本身是suggestion类型,与--fix自动修复机制相互独立。

延伸阅读

  • 规则源码:rules/prefer-default-parameters.js
  • 规则注册:rules/index.js
  • 测试用例(覆盖全部 valid/invalid 与 suggestion 输出):test/prefer-default-parameters.js
  • 相关 AST 工具:rules/ast/function-types.js、rules/ast/literal.js
  • 相关规则概念:ESLint 官方default-param-last(默认参数必须位于参数列表末尾的约束是本规则仅处理末位参数的原因之一)

【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn

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

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

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

立即咨询