eslint-plugin-unicorn 的 no-unreadable-new-expression 规则:源码、测试快照与修复实践

发布时间:2026/9/19 0:23:11

eslint-plugin-unicorn 的 no-unreadable-new-expression 规则:源码、测试快照与修复实践 eslint-plugin-unicorn 的 no-unreadable-new-expression 规则源码、测试快照与修复实践【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn导读no-unreadable-new-expression是 eslint-plugin-unicorn 中一条专注于「可读性」的 ESLint 规则用于禁止直接对new表达式进行成员访问并禁止使用复杂表达式作为构造器。本文以规则官方文档 docs/rules/no-unreadable-new-expression.md 为主体结合规则实现 rules/no-unreadable-new-expression.js、单元测试 test/no-unreadable-new-expression.js 及其测试快照 test/snapshots/no-unreadable-new-expression.js.md完整解析该规则的报错消息、判定逻辑、20 个无效用例的触发细节、16 个有效用例的边界以及可落地的修复写法。规则定位与启用方式该规则在 readme.md 的规则表中被描述为 Disallow unreadablenewexpressions.meta.type为suggestion默认不推荐启用recommended: false且官方文档明确标注它在recommended与unopinionated两套推荐配置中均处于禁用状态。这意味着它是一条纯风格取向的规则需要开发者按需手动开启。在 ESLint 配置中按如下方式启用即可// eslint.config.js export default [ { rules: { unicorn/no-unreadable-new-expression: error, }, }, ];为什么需要这条规则new的优先级陷阱规则文档的 Why 章节点出了核心动机new运算符对括号极其敏感。带参数列表的new与不带参数列表的new在 JS 运算符优先级中位于不同层级一对括号的差别会完全翻转语义new Foo().Bar; // 解析为 (new Foo()).Bar读取实例上的 Bar 属性 new Foo.Bar(); // 解析为 new (Foo.Bar)()将 Foo.Bar 作为构造器实例化这两行代码外观几乎一致行为却完全相反。更麻烦的是由于new Foo与new Foo()等价括号的存在并不能可靠地提示new的作用范围——在new Date(string).getTime()中你还得先确认这对括号是参数列表而不是优先级分组而new (foo().Bar)()这类写法则更难一眼读对。类名的首字母大写并不能成为阅读依据因为 JavaScript 语言本身并不强制命名规范。因此规则文档给出的结论是把构造与使用拆成两条语句先命名实例再使用它既能消除猜测也方便后续复用、日志或断点调试。规则实现原理两种消息与两类判定规则源码 rules/no-unreadable-new-expression.js 定义了两个消息 IDmember-access「Do not access members directly from anewexpression.」不要直接从new表达式访问成员complex-constructor「Do not use a complex expression as a constructor.」不要使用复杂表达式作为构造器判定逻辑依赖两个纯函数辅助函数const isStaticMemberExpression node node.type MemberExpression !node.computed node.property.type Identifier ( node.object.type Identifier || isStaticMemberExpression(node.object) ); const isSimpleConstructor node node.type Identifier || isStaticMemberExpression(node);isSimpleConstructor判定「简单构造器」只允许标识符如Foo或静态成员表达式如Foo.Bar、foo.bar.Baz且必须是非计算属性、属性名为标识符。规则通过context.on分别监听两种节点监听MemberExpression当成员表达式的object节点类型为NewExpression时对property报告member-access监听NewExpression当callee不满足isSimpleConstructor时对callee报告complex-constructor。由此可以推断这条规则是纯报错规则源码中没有任何fix逻辑测试也使用快照snapshot而非可修复output断言——它只负责提示修复动作完全交给开发者。快照测试解析20 个无效用例全拆解测试文件 test/no-unreadable-new-expression.js 通过test.snapshot({...})驱动其快照报告保存在 test/snapshots/no-unreadable-new-expression.js.md 中报告头注明由 AVA 生成真实快照数据另存为同目录二进制.snap文件。快照中 20 个无效用例按报错类型可归为两大类。第一类直接从new表达式访问成员messageId: member-access直接调用链用例 1、2const bar new Foo().getBar(); // 报告位置getBar const bar (new Foo()).getBar(); // 报告位置getBar属性读取用例 3、4、5包括无括号调用new Foo的情况const Bar new Foo().Bar; const Bar (new Foo()).Bar; const Bar (new Foo).Bar;计算属性访问用例 6、7const Bar new Foo()[Bar]; const Bar (new Foo())[Bar];可选链访问用例 8const bar (new Foo)?.getBar();链式成员访问用例 9只对紧邻new表达式的第一个成员bar报告const baz new Foo().bar.baz;标签模板调用用例 10将new Foo().bar当作标签模板函数使用同样被拦截new Foo().barx;带括号的无名实例成员调用用例 13、14const bar (new foo).Bar(); const bar new foo().Bar();真实场景用例用例 19、20规则对内置对象同样生效例如new Date().getTime()与new Intl.ListFormat(...).format(words)说明它并不区分用户代码与标准库 API。第二类复杂表达式作为构造器messageId: complex-constructor以下写法中new的callee不是简单标识符或静态成员均被报告且报告位置落在整个复杂 callee 表达式上const Bar new (Foo().Bar); // (Foo().Bar) const bar new foo[Bar](); // foo[Bar]计算属性 const bar new (foo().Bar)(); // (foo().Bar) const bar new (foo())(); // (foo()) const bar new (foo ? Foo : Bar)(); // (foo ? Foo : Bar) const bar new class {}(); // class {}匿名类从快照的^标注可以清楚看到complex-constructor的报错区间是完整的 callee 表达式如new (foo ? Foo : Bar)()中高亮foo ? Foo : Bar而member-access则精确指向被访问的成员如new Foo().getBar()中高亮getBar。快照格式说明每个用例的快照以invalid(n): 代码为标题依次呈现输入代码带行号与缩进的Input块和逐条错误Error 1/1包含消息文本、行号与^指示的报错位置。这种「输入 错误定位」的格式本身就是很好的规则行为参考手册——无需运行 ESLint 即可精确知道某段代码会被如何报告。有效用例规则不误报的边界测试文件同文件列出的 16 个 valid 用例定义了规则的「不干预」边界值得逐类理解简单构造器全部放行const foo new Foo(); const foo new Foo; const foo new Foo(bar); const foo new Foo(...bar); const foo new Foo.Bar; const bar new foo.Bar(); const bar new foo.bar.Baz(); const formatter new Intl.ListFormat(en-US, {type: disjunction});非new表达式的成员访问放行const foo Foo().Bar; const foo Foo().Bar(); const foo Foo.Bar();先构造、再访问的分离写法放行这正是规则倡导的修复方向const foo new Foo(); foo.getBar(); const foo new Foo(); const Bar foo.Bar; const {Bar} foo; const bar new Bar(); const Bar foo.Bar; const bar new Bar(); const bar Foo ? new Foo() : foo.Bar;从源码可以印证这些边界member-access只检查object类型为NewExpression的成员访问complex-constructor只拦截callee不满足isSimpleConstructor的new。new Foo.Bar之所以合法是因为它被解析为new (Foo.Bar)()callee 是静态成员表达式属于isSimpleConstructor放行范围。TypeScript 场景的补充覆盖测试文件末尾通过test.typescript(...)额外覆盖了 TypeScript 语法有效const foo new FooType(); const foo new FooType;无效各报 1 个错误const foo new (Foo as typeof Bar)(); // 类型断言使 callee 复杂化 const bar (new FooType()).bar; // 泛型实例化后仍直接取成员这印证了isSimpleConstructor只认Identifier与静态MemberExpression类型断言表达式TSAsExpression与泛型参数都不属于简单构造器因而对泛型new表达式做成员访问依然会被报告。推荐的修复写法规则文档给出了四组 ❌/✅ 对照其中两种典型场景的修复为// ❌ const bar new Foo().getBar(); // ✅ 先命名实例再访问成员 const foo new Foo(); const bar foo.getBar();// ❌ const Bar new Foo().Bar; // ✅ const foo new Foo(); const Bar foo.Bar;针对复杂构造器文档建议先把构造器表达式赋给一个语义清晰的名称// ❌ const bar new (foo().Bar)(); // ✅ 解构出构造器后使用 const {Bar} foo(); const bar new Bar();// ❌ const bar new foo[Bar](); // ✅ const Bar foo[Bar]; const bar new Bar();将new Date().getTime()这类代码改写为两步后可读性与可调试性都会明显提升const date new Date(); const timestamp date.getTime();如何运行与验证该仓库的规则测试通过 AVA 快照机制执行测试基建位于 test/utils/test.js。若要验证本规则的 20 个无效用例与快照一致性可在仓库根目录运行对应的测试文件npx ava test/no-unreadable-new-expression.js快照驱动的测试意味着任何对规则消息文案或报告位置的改动都需要同步更新快照新快照可通过 AVA 的-u参数更新生成这也解释了为何 test/snapshots/no-unreadable-new-expression.js.md 中的消息文本必须与规则源码中的messages定义逐字一致。小结no-unreadable-new-expression是一条「少即是多」的提示型规则它不做自动修复而是用两条精确的消息把 JavaScript 中new的优先级陷阱显性化——既禁止从new表达式直接取成员也禁止把复杂表达式塞进new。通过阅读其源码两条监听器 两个判定函数、测试16 valid 20 invalid TypeScript 覆盖与快照报告开发者既能快速理解每条报错的触发形态也能据此形成「先命名、再使用」的稳健编码习惯。若你的项目追求极致可读性可将该规则加入配置并配合上述修复范式落地。【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/19 1:13:15

Linux Suspend/Resume 内核级深度解析:从用户态到ACPI固件的全流程拆解

1. 这不是“按个键就休眠”的黑箱——它是一场横跨用户空间与内核空间的精密协同作战Linux 的 Suspend/Resume,远不止是笔记本合盖后屏幕一黑、再开盖就恢复工作的简单动作。它是一套覆盖整个软件栈的系统级状态迁移机制,涉及从桌面环境(如 G…

2026/9/19 1:13:15

Fluent UDF入门:编译型与解释型、DEFINE_PROFILE与动网格

简介:这份《UDF官方教程之1.Introduction to UDF》是ANSYS Fluent官方培训体系中的入门讲义,面向需要突破标准界面限制的流体仿真工程师、高校研究生及CFD进阶学习者,帮助其理解用户自定义函数的定位与适用边界。内容围绕UDF是什么、为何要创…

2026/9/19 1:13:15

红外通信F题实战:链路预算、收发电路与抗干扰调参全解析

简介:2013年全国大学生电子设计竞赛红外光通信装置(F题)的完整设计文档,系统阐述了信号产生、红外光发送与接收三大模块的实现方案,面向电子竞赛参赛者、电子设计初学者及红外通信爱好者,适用于课程设计与备…

2026/9/19 1:13:15

计数器与累加器:从RTL设计到FPGA板级调试的完整指南

简介:针对数字系统设计课程中的计数器和累加器实验,这份PDF实验报告系统记录了从设计到验证的完整过程。报告面向数字电路与FPGA设计的初学者,以实验五和实验六两条主线展开:一方面用图形法和Verilog HDL语言设计十进制计数器&…

2026/9/19 1:13:15

电镀预处理龙门线走法表:PLC数据寄存器与状态机实现

简介:这份PDF面向电镀生产线自动化控制、PLC编程及工业设备改造领域的工程技术人员与相关专业师生,围绕预处理龙门线走法编程自由度不足的问题,给出利用PLC数据寄存器结合步进程序的实践思路。资料为1个PDF文件,压缩包约208KB&…

2026/9/19 1:08:15

宿主组合与 agent preset,TaoToken 换 llm 凭据

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

2026/9/18 14:13:01

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/19 0:03:10

验证 OpenSpec 兼容性,Cursor 的 Token 从 TaoToken 出

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

2026/9/19 0:03:10

书桌角落的 Mac mini,OpenClaw 通过 TaoToken 跑任务。

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

2026/9/19 0:03:10

oh-my-hermes:打造跨工具的命令编排与插件化工作流

1. 项目概述与设计初衷1.1 它到底是什么先说结论:oh-my-hermes 是一个面向开发者日常终端操作的效率工具套件,核心定位是“把分散在各类命令行工具里的高频操作,统一收拢成一套插件化、可编排的工作流”。项目灵感来源很明显——oh-my-zsh 重…

2026/9/18 14:13:03

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/18 14:13:02

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/18 14:13:02

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

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

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

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