ESLint prefer-promise-reject-errors 规则深度解析:强制用 Error 对象作为 Promise 拒绝原因

发布时间:2026/11/11 2:00:35

ESLint prefer-promise-reject-errors 规则深度解析:强制用 Error 对象作为 Promise 拒绝原因 ESLint prefer-promise-reject-errors 规则深度解析强制用 Error 对象作为 Promise 拒绝原因【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint本篇技术指南围绕 ESLint 内置规则prefer-promise-reject-errors展开详细讲解该规则的设计动机、allowEmptyReject配置项、完整的不正确/正确代码示例并结合仓库源码剖析其 AST 检测原理与静态分析的固有局限。读完本文你将掌握如何在实际项目中配置并使用该规则理解它如何与no-throw-literal规则协同以及何时应当关闭它。规则背景为什么要用 Error 对象拒绝 Promise在 JavaScript 中Promise 的reject()函数可以接收任意值作为拒绝原因rejection reason。然而工程实践中有一条公认的良好实践对于用户自定义错误只向reject()传递内置Error对象的实例。这条规则的核心依据在于Error对象的一个重要特性它会自动记录构造时的堆栈追踪信息stack trace开发者可以借此定位错误究竟源自何处。相反如果 Promise 被一个非Error值如字符串、数字拒绝堆栈信息会丢失排查问题时将难以判断拒绝发生的具体位置——控制台只会显示Unhandled Promise Rejection: something bad happened这样近乎无用的信息。该规则在仓库中的元数据定义于 lib/rules/prefer-promise-reject-errors.js属于suggestion建议型规则默认不启用recommended: false不可自动修复fixable: null。其官方文档位于 docs/src/rules/prefer-promise-reject-errors.md。规则目标该规则旨在确保 Promise 只以Error对象作为拒绝原因。规则的核心消息为Expected the Promise rejection reason to be an Error.对应源码中的rejectAnError消息 IDmessages: { rejectAnError: Expected the Promise rejection reason to be an Error., },它同时覆盖两条检测路径Promise.reject(value)静态方法调用new Promise((resolve, reject) { reject(value) })构造器中调用第二个参数reject的情形。配置选项allowEmptyReject该规则接受一个可选的对象参数仅包含一个布尔选项选项类型默认值说明allowEmptyRejectbooleanfalse允许无参调用Promise.reject()/reject()在 lib/rules/prefer-promise-reject-errors.js 的defaultOptions中该选项的默认值被定义为{ allowEmptyReject: false }且 schema 规定该对象不允许出现其他额外属性schema: [ { type: object, properties: { allowEmptyReject: { type: boolean }, }, additionalProperties: false, }, ],补充说明无参的Promise.reject()实际会将undefined作为拒绝原因这也是为什么默认情况下Promise.reject()会被报告——它等价于拒绝了一个非Error值。不正确的代码示例以下代码在默认配置error级别下均会被报告/*eslint prefer-promise-reject-errors: error*/ Promise.reject(something bad happened); Promise.reject(5); Promise.reject(); new Promise(function(resolve, reject) { reject(something bad happened); }); new Promise(function(resolve, reject) { reject(); });正确的代码示例以下代码符合规则要求不会被报告/*eslint prefer-promise-reject-errors: error*/ Promise.reject(new Error(something bad happened)); Promise.reject(new TypeError(something bad happened)); new Promise(function(resolve, reject) { reject(new Error(something bad happened)); }); const foo getUnknownValue(); Promise.reject(foo);注意最后一个例子foo来自未知来源函数调用返回值静态分析无法确定它是否一定是非Error值因此规则选择不报告详见下文已知限制一节。开启 allowEmptyReject 后的正确代码示例/*eslint prefer-promise-reject-errors: [error, {allowEmptyReject: true}]*/ Promise.reject(); new Promise(function(resolve, reject) { reject(); });源码级原理规则是如何检测的深入阅读 lib/rules/prefer-promise-reject-errors.js可以看到该规则的实现由三个核心部分组成。1.checkRejectCall检查 reject 调用的实参这是规则的判定核心lib/rules/prefer-promise-reject-errors.js#L62-L81function checkRejectCall(callExpression) { if (!callExpression.arguments.length allowEmptyReject) { return; } const rejectionReason callExpression.arguments[0]; if ( !callExpression.arguments.length || !astUtils.couldBeError(rejectionReason) || (rejectionReason.type Identifier rejectionReason.name undefined sourceCode.isGlobalReference(rejectionReason)) ) { context.report({ node: callExpression, messageId: rejectAnError, }); } }报告条件有三种调用无参数且未开启allowEmptyReject第一个参数不可能是一个Error对象couldBeError返回false第一个参数是全局的undefined标识符这里用isGlobalReference排除了被局部遮蔽的undefined因为局部遮蔽的undefined理论上可能是一个Error对象。2.isPromiseRejectCall识别Promise.reject()调用lib/rules/prefer-promise-reject-errors.js#L88-L99function isPromiseRejectCall(node) { return ( astUtils.isSpecificMemberAccess( node.callee, Promise, reject, ) sourceCode.isGlobalReference( astUtils.skipChainExpression(node.callee).object, ) ); }这里有两层判断第一调用必须是Promise.reject这样的成员访问形式同时兼容可选链如Promise?.reject(5)测试中将其作为违规用例第二Promise必须是全局引用。也就是说如果代码中声明了局部变量遮蔽了全局Promise规则不会误报。这一点在测试文件 tests/lib/rules/prefer-promise-reject-errors.js 中有大量覆盖例如// 以下均为 valid不报告 /* global Promise:off */ Promise.reject(x), let Promise; Promise.reject(x);, function f(Promise) { return Promise.reject(x); }, { class Promise { static reject(x) { return x; } } Promise.reject(x); },3.NewExpression:exit监听器追踪构造器中的 reject规则通过监听NewExpression:exit来识别new Promise((resolve, reject) ...)lib/rules/prefer-promise-reject-errors.js#L118-L161。之所以选择:exit而非进入阶段是为了确保表达式内的节点已挂载parent属性便于后续沿引用关系回溯。其处理流程是确认callee是全局引用的Promise标识符且第一个参数是函数、参数个数大于 1、第二个参数是普通标识符因此解构形式的第二参数如function(resolve, {apply})不会被误判通过sourceCode.getDeclaredVariables找到与第二个参数同名的变量过滤出读取该变量的引用ref.isRead()过滤出作为函数调用 callee的引用即reject(...)形式对每个这样的调用执行checkRejectCall。值得注意的边界处理当new Promise构造器的第二个参数名与第一个相同如function(reject, reject)或与arguments同名时测试中均作为违规用例覆盖。从源码注释可以看到作者已论证重复参数名场景下不会出现变量被覆盖前就被读取的误判因为带重复参数的函数不允许在参数列表中使用解构或默认值。核心辅助函数couldBeError什么可能是 ErrorcheckRejectCall依赖的astUtils.couldBeError定义于 lib/rules/utils/ast-utils.js#L2529-L2594它采用宁可放过、不可误报的宽松策略对以下节点类型直接返回true即有可能是 Error 对象Identifier、CallExpression、NewExpression、MemberExpressionTaggedTemplateExpression、YieldExpression、AwaitExpression、ChainExpression对于复合表达式则递归判断赋值表达式和只看右侧||和??左右任一成立即可其余数学赋值运算符算术/位运算结果只能是原始值或抛异常故返回false序列表达式只关心最后一个表达式逻辑表达式只看右侧短路语义下左侧假值不可能是 Error||左右任一成立即可条件表达式consequent与alternate任一成立即可。正因如此Promise.reject(foo new Error())会被报告数学赋值结果不可能是 Error而Promise.reject(foo new Error())则合法——这些都被测试用例逐一验证。已知限制静态分析的边界由于静态分析的固有局限该规则无法保证你只会用Error对象拒绝 Promise。规则只会在能确定拒绝原因显然不是Error时报告当某个原因是否为Error存在不确定性时规则选择不报告。例如以下代码会通过该规则的检查但实际运行时拒绝原因并不是Errorconst err error; Promise.reject(err); // err 被推断为可能是 ErrorIdentifier不报告 function foo(bar) { return bar; } Promise.reject(foo(error)); // CallExpression 被视为可能不报告这一局限与no-throw-literal规则完全一致——后者同样基于couldBeError判断只禁止抛出不可能是Error的表达式字符串、数字、null、undefined等字面量。与 no-throw-literal 的分工为避免规则间冲突prefer-promise-reject-errors不会报告 async 函数中throw非 Error 值的情况——尽管这最终会导致 Promise 拒绝async 函数的throw会被转化为 rejected promise。这类场景应交给no-throw-literal规则处理async function foo() { throw something bad happened; // no-throw-literal 会报告prefer-promise-reject-errors 不处理 }两个规则在文档 front-matter 中被标记为related_rules关联关系见 docs/src/rules/prefer-promise-reject-errors.md 头部并都引用 Bluebird 的警告说明作为设计参考。实际配置建议在扁平配置flat config中启用该规则可以像下面这样在eslint.config.js中配置export default [ { rules: { prefer-promise-reject-errors: [error, { allowEmptyReject: false }], }, }, ];针对不同团队的取舍建议默认推荐保持allowEmptyReject: false强制所有拒绝都携带明确的Error对象最大化调试信息宽松场景如果代码中大量使用拒绝即代表流程终止的模式如取消操作可开启allowEmptyReject: true允许无参拒绝自定义拒绝值如果团队约定使用自定义的非Error值如业务错误码对象作为拒绝原因则应关闭此规则。When Not To Use It何时不使用如果你正在使用自定义的非Error值作为 Promise 拒绝原因可以直接关闭该规则。典型场景包括团队内部约定用普通对象{ code, message }表示业务错误、依赖第三方库返回特殊拒绝值、或者拒绝原因仅用于流程控制而不需要堆栈信息等。结语prefer-promise-reject-errors是 ESLint 内置的 suggestion 型规则它用一个简单的约定只用Error对象拒绝 Promise换取可追踪的堆栈信息显著降低异步错误排查成本。理解其基于couldBeError的可能即放过判定策略有助于准确把握它的报告边界而结合no-throw-literal一起使用则能覆盖同步与异步两条错误传播路径构建更完整的异常质量防线。延伸阅读仓库内规则实现源码lib/rules/prefer-promise-reject-errors.js规则单元测试tests/lib/rules/prefer-promise-reject-errors.js核心判定工具函数couldBeErrorlib/rules/utils/ast-utils.js关联规则no-throw-literal文档docs/src/rules/no-throw-literal.md全部内置规则索引lib/rules/index.js【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/11/6 6:53:36

计算与算计的辩证关系及技术应用

1. 标题解析与核心概念"计算是一种算计,算计也是一种计算"这个标题揭示了计算与算计之间微妙的辩证关系。从表面看,计算通常指数学运算或数据处理,而算计则带有策略谋划的意味。但深入思考会发现,两者本质都是对信息的系…

2026/11/6 9:22:00

CMSIS-NN源码尽调:从构建证据到验证边界

做嵌入式端侧 AI 有一段时间后,我养成了一个不太一样的习惯:拿到芯片厂商提供的神经网络库,先不看 README,也不急着调 demo,而是先把源码按“尽调”的方式过一遍。这次对 ARM CMSIS-NN 的源码尽调,起因很简…

2026/11/6 17:55:26

基于 Python 的宠物食品销售数据分析可视化系统的设计与实现

1. 引言随着宠物经济的快速发展,宠物食品市场规模持续扩大,消费者对宠物食品的品质、营养和品牌关注度不断提升。面对海量的销售数据,如何高效地完成数据采集、清洗、分析与可视化展示,成为企业制定营销策略、优化产品结构的重要基…

2026/11/11 1:57:33

重新考低压电工证怎么选?学历不高怕报不上名?上岗必备全攻略

重新考低压电工证怎么选?学历不高怕报不上名?上岗必备全攻略 学历不高怕报不上名?别慌,重新考低压电工证确实是很多一线工人的“上岗必备”,尤其是在建筑、电力、工程等岗位上,没有这个证,连面试资格都没有。我干了十年应急管理工作,见过太多人因为这…

2026/11/11 1:57:33

燕郊电工证在哪考多少钱?实操考试心里没底怕挂科怎么办?

燕郊电工证在哪考多少钱?实操考试心里没底怕挂科怎么办? 实操考试心里没底怕挂科,这是不少准备报考燕郊电工证的朋友最真实的感受。尤其是在当前政策收紧、考试难度加大的情况下,如何顺利拿到电工证成了大家的头等大事。而且, 多少钱…

2026/11/11 1:57:33

电工证档案查询全攻略 郑州报考避坑指南帮你稳过实操考试

电工证档案查询全攻略 郑州报考避坑指南帮你稳过实操考试 实操考试心里没底怕挂科?别急,今天就从一个在信阳浉河区干劳务班组的老实人角度,带你搞懂电工证档案查询和郑州报考的那些事儿。咱们不扯虚的,只讲干货,帮你避开那些“一不小心就踩坑”的地方。…

2026/11/11 1:57:32

花桥登高架设证培训合格标准与通过率,官方回应你关心的转证问题

花桥登高架设证培训合格标准与通过率,官方回应你关心的转证问题 之前考的证在外省能不能转过来?这个问题是很多在外地打工、在工地干活的老哥们最关心的。尤其是像我们这些在平顶山干水利工程的,经常需要来回跑工地,证书的通用性和转换流程就显得格外重要…

2026/11/11 1:57:32

廊坊登高架设证在哪办通过率揭秘:别让培训费打了水漂

廊坊登高架设证在哪办通过率揭秘:别让培训费打了水漂 怕考不过白交培训费?你不是一个人。廊坊登高架设证在哪办,这个问题让很多人在报名前纠结不已,尤其是刚入行的从业者。其实,了解清楚通过率和备考策略,能帮你少走弯路,省下一笔冤枉钱。这篇文章将从…

2026/11/11 1:52:32

长丰电工证考试地点在哪?多久拿证不花冤枉钱

长丰电工证考试地点在哪?多久拿证不花冤枉钱 怕考不过白交培训费?长丰电工证考试地点和拿证时间你必须了解清楚,别再被培训机构忽悠了。 现在电工证越来越重要,尤其在南阳卧龙区,中小型施工企业对持证上岗要求越来越严格,不少企业负责人反映,…

2026/11/10 8:12:38

电工证丢失还能补吗?千万别踩坑,丢了证等于失业

电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。…

2026/11/11 0:02:26

考低压电工证通过率考试真题与备考技巧

低压电工证通过率高不高?学历不高也能考,值不值得考? 你是不是总听说电工证能帮你找高薪工作,但又担心自己学历不够,考不上?其实,低压电工证考试门槛并没有你想象中那么高,关键是方法对不对。这篇文章就告诉你, 考低压电工证通过率到底有多高 ,…

2026/11/11 0:02:26

低压电工证学时千万别踩坑,濮阳毕业生怎么避坑报名

低压电工证学时千万别踩坑,濮阳毕业生怎么避坑报名 你是不是也和我一样,刚毕业就听说低压电工证学时必须达标才能上岗?可一打听,发现报名机构五花八门,价格参差不齐, 不知道去哪报名怕被中介坑…

2026/11/10 20:08:15

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/10 10:29:37

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑