深入解析 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 位 |
EvalError | 与eval()相关的错误 | 第 0 位 |
RangeError | 数值超出有效范围 | 第 0 位 |
ReferenceError | 引用不存在的变量 | 第 0 位 |
SyntaxError | 语法错误 | 第 0 位 |
TypeError | 类型不匹配 | 第 0 位 |
URIError | URI 处理错误 | 第 0 位 |
AggregateError | 聚合多个错误 | 第 1 位(首位为errors数组) |
SuppressedError | 表示被using声明抑制的错误 | 第 2 位(前两位分别为error、suppressed) |
大多数错误构造器的 message 是第一个参数,但AggregateError与SuppressedError的签名特殊,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(直接调用)与NewExpression(new调用)节点。整个判定流程可拆解为以下环节。
第一步:精确命中目标调用
规则借助插件自带的 isCallOrNewExpression 辅助函数完成目标匹配,并附加一个关键约束——必须是对全局内置构造器的引用:
context.on(['CallExpression', 'NewExpression'], expression => { if (!( isCallOrNewExpression(expression, { names: builtinErrors, optional: false, }) && context.sourceCode.isGlobalReference(expression.callee) )) { return; } // ... });两个条件缺一不可:
- 名称匹配:
callee的名字必须落在builtinErrors清单中; - 全局引用校验:通过
isGlobalReference确认该名字指向全局作用域的内置构造器。这意味着如果代码在局部作用域中定义了同名的Error、TypeError等变量(即 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-string | message 是空字符串''或空模板字符串``` | Error message should not be an empty string. |
message-is-not-a-string | message 静态可判定为字符串以外的类型 | Error message should be a string. |
其中missing-message的文案会通过data: {constructorName}动态插入实际的构造器名,例如Pass a message to the \TypeError` constructor.`。
第四步:静态值分析,穿透表达式看本质
这是规则最有技术含量的部分。拿到 message 参数节点后,规则并非简单地检查字面量类型,而是调用getStaticValueForNode进行静态值分析,尝试在不执行代码的前提下推导出该表达式的确定值。
针对ArrayExpression和ObjectExpression这类绝无可能等于字符串的节点,规则直接短路报告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声明的字面量且无潜在可变成员访问后,才允许对其属性做静态求值——这是出于副作用安全性的谨慎设计。
求值结果分三种去向:
- 静态值不存在(无法确定,如
new Error(foo)):规则保持沉默,宁可漏报也不误报; - 静态值不是字符串(如
new Error(false)、new Error(42)、new Error([0][0])):报告message-is-not-a-string; - 静态值是空字符串:报告
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),仅供参考