ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

ESLint no-throw-literal 规则深入解析:规范异常抛出的最佳实践与底层实现

ESLint no-throw-literal 规则深入解析:规范异常抛出的最佳实践与底层实现 ESLint no-throw-literal 规则深入解析规范异常抛出的最佳实践与底层实现【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslintno-throw-literal是 ESLint 核心库中的一条suggestion类型规则用于限制异常抛出throw语句的表达形式只允许抛出有可能成为Error对象的表达式。本指南以 docs/src/rules/no-throw-literal.md 官方文档为主体结合仓库中的规则实现、AST 工具函数与测试用例带你完整理解该规则的判定边界、配置方式、已知限制及源码级实现原理读完即可在自己的项目中正确启用并规避误用。规则背景与设计初衷JavaScript 语言本身允许throw任意值包括字符串、数字、null、undefined甚至普通对象。但社区普遍认为只抛出Error对象本身、或基于Error对象派生的自定义异常对象才是良好的实践。Error对象的核心优势在于它会自动记录自身被创建和抛出的位置即调用栈信息包括文件与行号。当异常在深层调用链中被捕获并重新抛出时这些信息对定位问题根源至关重要而抛出字符串等字面量则完全丢失了这些上下文。该规则最早创建时只禁止抛出字面量因此得名no-throw-literal但如今已扩展为只允许抛出有可能成为 Error 对象的表达式同时仍保留了对字面量的拦截能力。在仓库中该规则注册于 lib/rules/index.js类型声明见 lib/types/rules.d.ts其文档元数据中recommended: false即不包含在eslint:recommended预设中需要用户显式开启。规则详情哪些代码会被判定为违规该规则的目的是在抛异常时保持一致性禁止抛出字面量以及其他不可能成为Error对象的表达式。以下代码均为**错误incorrect**示例/*eslint no-throw-literal: error*/ throw error; // 字符串字面量 throw 0; // 数字字面量 throw undefined; // 全局 undefined throw null; // null 字面量 const err new Error(); throw an err; // 字符串拼接err 被隐式转为字符串字面量 const err2 new Error(); throw ${err2} // 模板字符串同样被转换为字符串需要特别注意的是后两个例子即使参与运算的是Error对象一旦发生字符串拼接或模板字符串插值Error对象会被强制转换为字符串最终抛出的仍是字符串因此同样会被规则拦截。以下代码均为**正确correct**示例/*eslint no-throw-literal: error*/ throw new Error(); // 直接抛出 Error 实例 throw new Error(error); // 携带错误信息的 Error 实例 const e new Error(error); throw e; // 抛出变量可能是 Error 对象 try { throw new Error(error); } catch (e) { throw e; // 捕获后重新抛出保持原始错误 }Options 配置项该规则没有任何配置项。其 schema 为空数组见 lib/rules/no-throw-literal.js 的schema: []意味着只能以error、warn或off三档严重级别开启无需也无法传入任何参数。典型配置写法// eslint.config.js扁平配置 export default [ { rules: { no-throw-literal: error, // 或 warn }, }, ];由于该规则不可配置它实际是一个开/关型规则——开启后即按文档所述的统一判定逻辑执行。已知限制静态分析的边界受限于静态分析的固有局限该规则无法保证你只会抛出Error对象。它只能做语法形状层面的判断无法在运行期确认真实值类型。以下代码在规则看来是**正确不会报错**的但运行时抛出的其实并不是Error对象/*eslint no-throw-literal: error*/ const err error; // 变量名为 err但值是字符串 throw err; function foo(bar) { console.log(bar); } throw foo(error); // 函数调用可能返回任何值 throw new String(error); // String 包装对象并非 Error const baz { bar: error }; throw baz.bar; // 成员表达式运行时值是字符串这些示例暴露了规则判定的保守策略对于标识符、函数调用、成员表达式等形状上可能产生对象的节点规则一律放行返回可能是 Error宁可漏报也不误报。这一点在下面的源码实现中可以得到完全印证。源码实现couldBeError 判定算法规则的完整实现位于 lib/rules/no-throw-literal.js核心逻辑非常精简——它监听每个ThrowStatement节点将throw后的表达式交给astUtils.couldBeError()判断ThrowStatement(node) { if (!astUtils.couldBeError(node.argument)) { context.report({ node, messageId: object }); } else if (node.argument.type Identifier) { if ( node.argument.name undefined sourceCode.isGlobalReference(node.argument) ) { context.report({ node, messageId: undef }); } } },该规则定义了两种错误消息见 lib/rules/no-throw-literal.jsobjectExpected an error object to be thrown.抛出对象不可能为 Error主路径报错undefDo not throw undefined.抛出了全局undefined特殊处理couldBeError 的分支判定couldBeError定义于 lib/rules/utils/ast-utils.js是一个基于 AST 节点类型的模式匹配函数判定该节点是否有成为Error对象的可能性直接判定为可能的节点类型放行节点类型说明Identifier变量引用运行期值未知可能是 ErrorCallExpression函数调用可能返回 ErrorNewExpression构造调用如new Error()MemberExpression成员访问如foo.bar、foo[bar]TaggedTemplateExpression带标签的模板字符串标签函数可能返回对象YieldExpression/AwaitExpression生成器产出 / 异步等待的值ChainExpression可选链表达式如obj?.foo按运算符递归判断的节点类型AssignmentExpression赋值表达式与只看右侧操作数是否可能为 Error||与??左右两侧任一可能为 Error 即放行因为短路时可能取到左侧原值其余算术/位运算赋值符、等一律判定为不可能——这类表达式要么求出原始值要么在求值过程中直接抛出无论如何都不会得到 Error 对象。源码注释明确说明了这一设计决策。SequenceExpression逗号表达式整体结果取决于最后一个表达式因此只检查exprs.at(-1)。LogicalExpression逻辑表达式若左侧为假值则短路假值不可能是 Error所以只检查右侧||/??两侧任一可能为 Error 即放行。源码注释还指出未来可改进为通过排除假值字面量来验证左侧可能为真值。ConditionalExpression三元表达式consequent与alternate任一可能为 Error 即放行。其余所有节点类型Literal、TemplateLiteral、ObjectExpression等默认返回false即判定为不可能是 Error 对象从而触发object报错。这正是文档中字符串、数字、null、模板字符串、对象字面量、字符串拼接等写法全部被拦截的原因。undefined 的特判逻辑规则对undefined有独立于couldBeError的处理由于Identifier类型本身被couldBeError放行规则又叠加了一层检查——当throw的表达式是标识符且名为undefined时调用sourceCode.isGlobalReference()实现见 lib/languages/js/source-code/source-code.js确认它引用的是全局undefined而非局部同名变量function foo(undefined) { throw undefined; } // 局部参数不报错测试中的 valid 用例如果undefined是函数参数、局部变量等局部绑定则不属于抛出全局 undefined不会触发undef报错——这体现了规则对作用域的精细处理。测试用例判定边界的完整印证规则测试位于 tests/lib/rules/no-throw-literal.js覆盖了上述所有分支可作为理解规则行为的行为规范valid放行用例精选throw new Error();、throw Error(error);、throw e;—— 标准 Error 抛出throw foo new Error();——赋值右侧可能为 Errorthrow foo.bar || literal、throw foo[bar] ?? literal—— 逻辑赋值运算符短路放行需ecmaVersion: 2021throw 1, 2, new Error();—— 逗号表达式取最后一项throw literal new Error();——只看右侧throw foo ? new Error() : literal;—— 三元表达式任一分支可能为 Errorthrow tag${foo};、throw yield index;、throw await bar;、throw obj?.foo—— 分别对应 TaggedTemplate / Yield / Await / Chain 表达式需对应ecmaVersionfunction foo(undefined) { throw undefined; }—— 局部undefined不报错invalid报错用例精选均断言messageIdthrow error;、throw 0;、throw false;、throw null;、throw {};—— 各类字面量与普通对象objectthrow undefined;—— 全局 undefinedundefthrow a b;、var b new Error(); throw a b;—— 字符串拼接产物objectthrow foo error;—— 赋值右侧是字面量objectthrow foo new Error();、throw foo new Error();—— 算术/位运算赋值objectthrow foo literal——右侧非 Errorobjectthrow new Error(), 1, 2, 3;—— 逗号表达式最后一项是字面量objectthrow literal not an Error;、throw foo literal——右侧非 Errorobjectthrow foo ? not an Error : literal;—— 两个分支都不是 Errorobjectthrow${err};—— 模板字符串object这些用例与couldBeError的每个分支一一对应实际执行时可作为规则的活文档帮助你预判自己的业务代码是否会被拦截。在项目中的实践建议基于规则行为与静态分析边界实际使用时有几点建议建议在项目根级配置中开启由于不属于eslint:recommended需在eslint.config.js中显式添加no-throw-literal: error才能生效。与自定义错误类型配合使用让自定义异常继承Error如class MyError extends Error {}既符合规则要求又能保留完整调用栈。警惕间接抛字面量throw an err、throw \${err2} 这类写法会被规则捕获但它无法识别函数返回字符串后抛出、成员属性为字符串等间接情况见已知限制仍需代码评审兜底。该规则无 autofixno-throw-literal不提供自动修复能力报告的错误需要手工改写为throw new Error(...)形式。如果你希望深入了解规则判定算法在更复杂表达式上的行为直接阅读 lib/rules/utils/ast-utils.js 中couldBeError的完整注释其中包含了短路、数学赋值等设计的详细说明再对照 tests/lib/rules/no-throw-literal.js 逐条验证即可对该规则建立完整的认识。【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表