资讯详情

ESLint require-yield 规则详解:禁止生成器函数缺少 `yield` 关键字

📅 2026/9/13 2:03:47 | 华诺云谱 👁 阅读
ESLint require-yield 规则详解:禁止生成器函数缺少 `yield` 关键字
ESLint require-yield 规则详解禁止生成器函数缺少yield关键字【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint导读require-yield是 ESLint 内置的 JavaScript 语法检查规则用于检测声明为生成器函数generator function却从未包含yield表达式的代码并给出警告。这类函数通常是重构或误用function*语法的结果一个用*声明的函数如果没有任何yield就失去了生成器按需产出值的意义。读完本文你将掌握该规则的完整语义、正确与错误写法、在源码层的判定原理包括嵌套生成器、空函数豁免、报告位置定位以及它为何被纳入eslint:recommended默认推荐配置、如何在实际项目中启停它。本文以仓库中的规则文档 docs/src/rules/require-yield.md 为核心骨架并结合 lib/rules/require-yield.js 源码实现、tests/lib/rules/require-yield.js 测试用例进行纵深佐证。规则概述项目内容规则名称require-yield规则类型suggestion建议型是否推荐是已包含在eslint:recommended中是否可自动修复否是否提供建议suggestions否是否有可配置选项否schema: []无任何选项建议关闭场景不想提示缺少yield的生成器函数时规则的官方描述为Require generator functions to containyield要求生成器函数包含yield对应的文档元信息定义在 lib/rules/require-yield.js 的meta块中meta: { type: suggestion, docs: { description: Require generator functions to contain yield, recommended: true, url: https://eslint.org/docs/latest/rules/require-yield, }, schema: [], messages: { missingYield: This generator function does not have yield., }, },这里有两个值得注意的事实docs.recommended: true表示该规则属于 ESLint 推荐规则集。查看仓库中的推荐配置 packages/js/src/configs/eslint-recommended.js可以看到require-yield在eslint:recommended中以error级别默认开启它同时也出现在 packages/js/src/configs/eslint-all.js 中作为全部规则配置的一部分。schema: []表明该规则不接受任何配置选项因此它的行为是确定且唯一的不存在任何参数调优空间。Rule Details规则的判定逻辑根据文档该规则会对所有不包含yield关键字的生成器函数产生警告。理解它之前先明确生成器函数的形态——在 JavaScript 中使用function*语法声明的函数即为生成器函数例如function* foo() { yield 5; return 10; }生成器函数的核心特征是可以使用yield表达式逐次产出值调用者通过迭代器iterator按需获取。如果一个函数声明为生成器却从头到尾不使用yield那么它很可能是一个错误或多余的写法。错误的代码示例应被警告以下代码中foo被声明为生成器函数但函数体内只有return 10;没有任何yield表达式因此会触发require-yield的警告::: incorrect/*eslint require-yield: error*/ function* foo() { return 10; }:::规则触发时产生的错误消息为missingYield文案为This generator function does not have yield.正确的代码示例不应被警告以下几类写法都是该规则认可的正确用法::: correct/*eslint require-yield: error*/ // 生成器函数中包含 yield符合要求 function* foo() { yield 5; return 10; } // 普通函数不适用本规则即使没有 yield 也不警告 function bar() { return 10; } // 空生成器函数不会被警告 function* baz() { }:::这三段示例恰好对应规则的三个关键豁免场景包含yield的生成器函数函数体内至少存在一个yield表达式即使同时存在return也合法。普通函数规则只关注生成器函数node.generator为true的函数节点普通函数完全不受影响。空生成器函数function* baz() { }虽然没有任何语句但规则设计上选择不警告空函数源码中通过node.body.body.length 0判断详见下文实现解析。源码实现解析规则如何在内部工作为了让上述行为变得可验证、可预期我们深入阅读 lib/rules/require-yield.js 的完整实现。整个规则只有 85 行左右核心思想是用一个计数栈stack按函数作用域统计yield出现次数。整体流程进入函数入栈、退出函数出栈并报告create(context) { const stack []; const sourceCode context.sourceCode; function beginChecking(node) { if (node.generator) { stack.push(0); } } function endChecking(node) { if (!node.generator) { return; } const countYield stack.pop(); if (countYield 0 node.body.body.length 0) { context.report({ loc: astUtils.getFunctionHeadLoc(node, sourceCode), messageId: missingYield, }); } } return { FunctionDeclaration: beginChecking, FunctionDeclaration:exit: endChecking, FunctionExpression: beginChecking, FunctionExpression:exit: endChecking, // Increases the count of yield keyword. YieldExpression() { if (stack.length 0) { stack[stack.length - 1] 1; } }, }; }其工作机理可以拆解为四步遍历函数节点规则通过选择器监听FunctionDeclaration函数声明与FunctionExpression函数表达式两类节点在进入:exit之前与退出时分别执行beginChecking与endChecking。仅统计生成器beginChecking中先判断node.generator是否为真只有生成器函数才会向stack压入一个初始值为0的计数。累计 yield 次数每当遇到YieldExpression节点就把栈顶计数加 1。这里利用了栈顶即当前正在遍历的最内层函数这一天然关系因此yield只会计入它所在的函数。退出时判定并报告endChecking弹出该函数对应的计数。当满足countYield 0且node.body.body.length 0两个条件时才调用context.report发出missingYield警告。空函数豁免的精确含义文档中空生成器函数不警告的表述在源码中体现为node.body.body.length 0这一条件函数体语句数组长度为 0即空块{ }时不报告。注意这里的判断对象是函数体的语句列表因此以下看似空、实则有语句的情况依然会被警告function* foo() { return 10; // body.body.length 1非空 → 警告 }报告位置的定位指向函数头部警告信息并不是挂在整棵函数节点上而是精确定位到函数头部从function关键字到参数左括号(之前这是通过astUtils.getFunctionHeadLoc(node, sourceCode)实现的。该辅助函数定义在 lib/rules/utils/ast-utils.js它会根据函数节点的父节点类型做差异化处理当父节点是Property、MethodDefinition、PropertyDefinition、TSPropertySignature、TSMethodSignature等即对象方法、类方法等场景时起点取父节点的loc.start终点取参数左括号位置其他一般函数节点直接以node.loc.start作为起点。这意味着无论yield缺失发生在函数声明、函数表达式、对象方法还是类方法中报告都能精确指向function* foo这一头部区域方便开发者一眼定位问题。嵌套生成器栈结构如何保证互不干扰计数栈stack而非单一计数变量是本规则正确处理嵌套函数的关键。例如function* foo() { function* bar() { yield 0; } }当遍历进入foo时栈为[0]进入内层bar时栈变为[0, 0]yield 0只把栈顶bar的计数加 1栈变为[0, 1]退出bar时弹出1不满足countYield 0因此bar不被警告随后退出foo时弹出的是它自己的计数0且函数体非空于是foo被警告。这正是文档没有显式展开、但由源码结构可以明确推断出的边界行为。测试用例佐证仓库中的 tests/lib/rules/require-yield.js 使用RuleTester来自 lib/rule-tester/rule-tester.js对该规则进行了系统性验证可作为理解规则行为的活文档。合法用例valid测试在ecmaVersion: 6的语言选项下运行生成器是 ES2015 语法以下代码均不触发警告function foo() { return 0; } // 普通函数 function* foo() { yield 0; } // 有 yield 的生成器 function* foo() { } // 空生成器 (function* foo() { yield 0; })(); // 立即执行的生成器表达式 (function* foo() { })(); // 空的生成器表达式 var obj { *foo() { yield 0; } }; // 对象方法 var obj { *foo() { } }; // 空对象方法 class A { *foo() { yield 0; } }; // 类方法 class A { *foo() { } }; // 空类方法这些用例覆盖了函数声明、函数表达式、对象方法、类方法四种生成器形态并逐一验证有 yield与空函数两种免警告情况。非法用例invalid与报告位置断言非法用例除了断言messageId: missingYield外还会精确断言报告位置。例如{ code: function* foo() { return 0; }, errors: [ { messageId: missingYield, line: 1, column: 1, endLine: 1, endColumn: 14, // 覆盖 function* foo() 的头部 }, ], },其他非法用例还验证了(function* foo() { return 0; })();函数表达式报告从第 2 列左括号后开始var obj { *foo() { return 0; } }对象方法报告从第 13 列开始class A { *foo() { return 0; } }类方法报告从第 11 列开始function* foo() { function* bar() { yield 0; } }外层foo无 yield、内层bar有 yield只警告外层function* foo() { function* bar() { return 0; } yield 0; }外层foo有 yield、内层bar无 yield只警告内层报告落在第 19 列。最后两个用例是嵌套生成器场景的官方测试印证直接验证了上文对计数栈行为的分析每个生成器函数独立计数互不影响。Options规则没有选项文档明确写道This rule has no options.对应源码中schema: []。因此你无法通过任何配置参数改变它的判定行为只能在启用与禁用之间选择。这与其他无选项规则如require-await其schema同样为空保持一致。与 require-await 的关联require-yield的文档头部front matter声明了相关规则require-await反之 docs/src/rules/require-await.md 也将require-yield列为相关规则。两者在语义上高度对称require-yield警告声明为生成器function*却没有yield的函数require-await警告声明为异步async function却没有await的函数。它们共同反映同一个工程判断为函数选择的关键字修饰符应当与函数体内实际使用的操作符匹配。如果async函数中没有awaitfunction*生成器中没有yield那么这种声明很可能是重构残留或笔误值得提醒。需要特别注意的是两者在豁免规则上并不完全相同require-yield豁免空生成器函数function* foo() { }不警告require-await同样豁免空异步函数但它明确忽略异步生成器函数async generator functions因为异步生成器可能通过yield*委托产出另一个异步生成器的全部值而无需直接使用await。这一差异在 docs/src/rules/require-await.md 中有明确说明。此外从实现细节上看两者还有一个显著区别require-await在 lib/rules/require-await.js 中设置了hasSuggestions: true可以提供移除async关键字的修复建议而require-yield没有任何自动修复或建议能力它甚至无法安全地建议移除*因为那可能改变语义。如何配置与使用通过推荐配置启用由于require-yield属于eslint:recommended推荐规则集使用 ESLint 的 flat config 时只需引入 JS 语言的推荐配置即可默认以error级别开启// eslint.config.js import js from eslint/js; export default [ js.configs.recommended, // 其他自定义配置…… ];对应的底层配置可见 packages/js/src/configs/eslint-recommended.jsrequire-yield: error,显式配置规则级别你也可以在配置中直接针对该规则设置级别error、warn或off// eslint.config.js export default [ { rules: { require-yield: error, }, }, ];由于规则不接受选项规则值只能是严重级别字符串或数字不能传选项对象。在代码中使用行内注释与文档示例一致也可以在文件内通过注释控制/*eslint require-yield: error*/ function* foo() { return 10; // 该行所在函数会触发警告 }When Not To Use It何时应关闭此规则文档给出了唯一一条关闭建议如果你不希望提示那些没有yield表达式的生成器函数那么关闭该规则是安全的。具体而言当你的团队确实存在有意使用无yield的生成器函数的合理场景时可以关闭它。例如生成器函数被用作某种占位或约定俗成的标记少见但存在函数通过yield*之外的机制如手动返回迭代器实现迭代协议不需要yield迁移或兼容代码中需要保留function*语法。关闭方式// eslint.config.js export default [ { rules: { require-yield: off, }, }, ];小结require-yield是一条简单但实用的语法语义规则生成器函数必须包含至少一个yield表达式否则发出suggestion类型的警告。它的核心要点可以总结为只针对function*声明或表达式形式的生成器函数普通函数完全不受影响空生成器函数function* foo() { }被豁免但包含return等语句的假空函数会被警告通过源码级计数栈实现嵌套生成器各自独立计数yield只计入其直接所属的函数无任何可配置选项也不提供自动修复仅通过eslint:recommended默认以error开启与require-await构成一对修饰符与操作符匹配的互补检查。对生成器语法尚不熟悉的开发者来说该规则能第一时间发现声明了生成器却忘了写yield的低级错误对已经充分理解生成器语义的团队而言它又是一道防止重构残留的廉价保险。进一步阅读规则文档docs/src/rules/require-yield.md规则源码lib/rules/require-yield.js规则测试tests/lib/rules/require-yield.js辅助函数getFunctionHeadLoclib/rules/utils/ast-utils.js相关规则文档docs/src/rules/require-await.md推荐配置eslint:recommendedpackages/js/src/configs/eslint-recommended.js【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。