深入解析 eslint-plugin-unicorn 的 error-message 规则:强制内置 Error 构造器必须携带 message
2026/9/18 3:28:32 网站建设 项目流程

深入解析 eslint-plugin-unicorn 的 error-message 规则:强制内置 Error 构造器必须携带 message

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

本篇技术指南聚焦于 eslint-plugin-unicorn 项目中的error-message规则,讲解它如何强制开发者在创建内置Error对象时传入有意义的message字符串,从而提升代码的可读性与可调试性。文中将完整覆盖该规则的判定示例、覆盖的内置错误构造器清单、三种违规类型,并结合 rules/error-message.js 的源码实现与 test/error-message.js 的测试用例,深入剖析其静态值分析、参数位置判定等底层原理。读完本文,你将掌握该规则的全部行为边界、启用方式,以及如何在项目中快速接入。

规则定位:让每一个内置错误都有话可说

error-message规则的核心目标是:当代码创建内置Error对象时,强制传入一个message。空参数或空字符串的异常对象在排查问题时几乎没有任何信息量,而携带明确 message 的错误能让日志、监控与调试过程一目了然。

该规则被标记为problem类型(意味着它检测的是可能引发实际问题的代码),并被收录在插件内置的recommended(推荐)与unopinionated(非强制观点)两套预设配置中,规则描述见 rules/error-message.js:

  • ✅ 默认随recommended配置启用;
  • ☑️ 同时包含于unopinionated配置。

规则本身不提供任何可配置选项,启用即生效,判定逻辑完全由源码内置。

覆盖的内置错误构造器清单

规则并非针对所有构造函数,而是只检查 JavaScript 运行时提供的内置错误类型。完整清单定义在 rules/shared/builtin-errors.js:

构造器说明message 参数位置
Error通用错误基类第 0 位
EvalErroreval()相关的错误第 0 位
RangeError数值超出有效范围第 0 位
ReferenceError引用不存在的变量第 0 位
SyntaxError语法错误第 0 位
TypeError类型不匹配第 0 位
URIErrorURI 处理错误第 0 位
AggregateError聚合多个错误第 1 位(首位为errors数组)
SuppressedError表示被using声明抑制的错误第 2 位(前两位分别为errorsuppressed

大多数错误构造器的 message 是第一个参数,但AggregateErrorSuppressedError的签名特殊,message 分别位于第 1、第 2 个参数位置。这一差异在源码中通过 messageArgumentIndexes 映射表 精确建模:

const messageArgumentIndexes = new Map([ ['AggregateError', 1], ['SuppressedError', 2], ]);

完整判定示例:什么被禁止、什么被允许

以下是规则文档中的全部官方示例,均直接继承自 docs/rules/error-message.md,可直接用于理解规则行为。

基础错误类型

// ❌ 缺少 message throw new Error(); // ❌ message 为空字符串 throw new Error(''); // ✅ 带有描述性 message throw new Error('Unexpected property.');

TypeError 等具体错误类型

// ❌ throw new TypeError(); // ✅ throw new TypeError('Array expected.');

AggregateError(message 是第二个参数)

// ❌ 只有 errors 数组,缺少 message const error = new AggregateError(errors); // ✅ 传入 message const error = new AggregateError(errors, 'Promises rejected.');

SuppressedError(message 是第三个参数)

// ❌ 只有 error 与 suppressed,缺少 message const error = new SuppressedError(error, suppressed); // ✅ const error = new SuppressedError(error, suppressed, 'This is a suppressed error.');

规则工作原理:源码级剖析

规则的实现位于 rules/error-message.js,其核心create函数在 L91-L154,监听 AST 中的CallExpression(直接调用)与NewExpressionnew调用)节点。整个判定流程可拆解为以下环节。

第一步:精确命中目标调用

规则借助插件自带的 isCallOrNewExpression 辅助函数完成目标匹配,并附加一个关键约束——必须是对全局内置构造器的引用

context.on(['CallExpression', 'NewExpression'], expression => { if (!( isCallOrNewExpression(expression, { names: builtinErrors, optional: false, }) && context.sourceCode.isGlobalReference(expression.callee) )) { return; } // ... });

两个条件缺一不可:

  • 名称匹配callee的名字必须落在builtinErrors清单中;
  • 全局引用校验:通过isGlobalReference确认该名字指向全局作用域的内置构造器。这意味着如果代码在局部作用域中定义了同名的ErrorTypeError等变量(即 shadowing 遮蔽),规则会主动放行,避免误报。测试用例 test/error-message.js 中的const Error = function () {}; new Error({...})正是这一场景的验证。

同时,optional: false意味着可选链调用(如Error?.())不会被命中。

第二步:按构造器类型定位 message 参数

拿到constructorName后,规则根据前面的映射表确定 message 应处的参数下标,再取出该位置的节点:

const constructorName = expression.callee.name; const messageArgumentIndex = messageArgumentIndexes.has(constructorName) ? messageArgumentIndexes.get(constructorName) : 0;

若该位置不存在参数节点,直接报告missing-message违规(整个表达式作为报告节点,并附带构造器名称用于文案插值)。

第三步:三种违规类型与报错文案

规则定义了三个 messageId,对应三种不同的违规形态,文案定义在 rules/error-message.js:

messageId触发条件报错文案
missing-message根本没有传入 message 参数Pass a message to the \{{constructorName}}` constructor.`
message-is-empty-stringmessage 是空字符串''或空模板字符串```Error message should not be an empty string.
message-is-not-a-stringmessage 静态可判定为字符串以外的类型Error message should be a string.

其中missing-message的文案会通过data: {constructorName}动态插入实际的构造器名,例如Pass a message to the \TypeError` constructor.`。

第四步:静态值分析,穿透表达式看本质

这是规则最有技术含量的部分。拿到 message 参数节点后,规则并非简单地检查字面量类型,而是调用getStaticValueForNode进行静态值分析,尝试在不执行代码的前提下推导出该表达式的确定值。

针对ArrayExpressionObjectExpression这类绝无可能等于字符串的节点,规则直接短路报告message-is-not-a-string,注释中明确说明这是getStaticValue可能无法识别其值的兜底处理(L123-L130):

if (node.type === 'ArrayExpression' || node.type === 'ObjectExpression') { return {node, messageId: MESSAGE_ID_NOT_STRING}; }

随后,getStaticValueForNode(L72-L86)会区分三种情况走不同的静态求值路径:

  • 普通表达式:直接调用 eslint-utils 的getStaticValue求值;
  • 分支表达式(如condition ? {} : 'ok'):调用getStaticValueForControlFlow做控制流敏感分析;
  • 可能含有可变成员访问的表达式:调用getStaticValueIfNoSideEffects,确保求值过程不误判带副作用或不可靠的属性读取。

此外还有一处对Object.freeze的特判:isObjectFreezeMemberExpression(L49-L70)与isSafeObjectFreezeArgument(L26-L47)会识别Object.freeze({...}).xxx这类被冻结对象上的属性访问,确认冻结对象是const声明的字面量且无潜在可变成员访问后,才允许对其属性做静态求值——这是出于副作用安全性的谨慎设计。

求值结果分三种去向:

  1. 静态值不存在(无法确定,如new Error(foo)):规则保持沉默,宁可漏报也不误报;
  2. 静态值不是字符串(如new Error(false)new Error(42)new Error([0][0])):报告message-is-not-a-string
  3. 静态值是空字符串:报告message-is-empty-string

测试用例 test/error-message.js 中的throw new Error(false)const condition = true; let value; new Error(condition ? {} : value);等均验证了这些分支。

第五步:对 SpreadElement 主动放行

如果 message 参数位置之前存在展开运算符(SpreadElement),规则无法确定实际参数个数,会直接返回、不做检查(L109-L112):

// If message is `SpreadElement` or there is `SpreadElement` before message if (callArguments.some((node, index) => index <= messageArgumentIndex && node.type === 'SpreadElement')) { return; }

因此new Error(...foo)new AggregateError(...foo, "")这类写法均属于有效用例,不会触发任何报告。

边界行为汇总:从测试用例看规则倾向

测试文件 test/error-message.js 通过 248 行用例(含快照)对规则进行了全面覆盖,从中可以提炼出几条明确的边界倾向:

场景判定结果原因
new Error()/Error()(直接调用不写new❌ 违规两种调用形态都检查
new MyCustomError()(自定义错误)✅ 放行只检查内置构造器
new Error(foo)(变量值不可静态确定)✅ 放行静态分析无法确定
new Error(...foo)(展开参数)✅ 放行参数个数不可确定
局部遮蔽Error后调用✅ 放行isGlobalReference校验
const err = new Error(); throw err;(赋值后抛出)❌ 违规创建处的NewExpression仍会被检查
new Error("message", 0, 0)(多余参数)✅ 放行只关心 message 参数本身
包含Object.freeze的可疑成员访问视冻结安全性而定有专门的冻结对象安全分析

另外值得注意的是,TypeScript 场景同样受支持:规则会通过unwrapTypeScriptExpression剥离as any等类型断言后再做静态求值(L73),测试中new Error((modes.size ? {} : 'ok') as any)即为此类用例;规则声明的languages['js/js']

如何在项目中启用

error-message规则随插件的预设配置默认开启,无需额外配置。如果你的项目使用扁平化配置(flat config),可参考仓库根目录的 eslint.config.js 中接入插件预设的方式;若采用旧的.eslintrc风格,则通过extends: ['plugin:unicorn/recommended']即可自动启用。由于规则不接收任何 options,启用后行为即固定,无需也无法做自定义调整。

如果希望单独显式声明该规则,可以直接按规则名引用(规则注册于 rules/index.js):

'unicorn/error-message': 'error',

相关代码路径速查

  • 规则文档:docs/rules/error-message.md
  • 规则实现:rules/error-message.js
  • 内置错误构造器清单:rules/shared/builtin-errors.js
  • 调用/新建表达式匹配辅助:rules/ast/call-or-new-expression.js
  • 静态值分析工具:rules/utils/get-static-value.js
  • 测试用例:test/error-message.js

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

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

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

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

立即咨询