eslint-plugin-unicorn 的 no-incorrect-template-string-interpolation 规则:从快照测试解读错误插值语法检测
2026/9/18 10:53:41 网站建设 项目流程

eslint-plugin-unicorn 的 no-incorrect-template-string-interpolation 规则:从快照测试解读错误插值语法检测

【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn

导读

no-incorrect-template-string-interpolation是 eslint-plugin-unicorn 中用于捕获模板字符串(Template Literal)错误插值语法的一条规则,它专门识别把${expression}误写成{name}$name}的常见手误。本文以该规则的快照测试报告(test/snapshots/no-incorrect-template-string-interpolation.js.md)为主体,逐条解读其 21 个错误用例的检测行为与自动修复建议,并结合规则源码与测试用例说明其判定边界、排除逻辑与配置方式,帮助你在实际项目中安全启用并理解其行为。

规则定位:纠正模板字符串插值语法手误

ES 的模板字符串插值语法是${expression},形如:

const greeting = `Hello ${name}`;

当开发者(尤其是习惯 PHP$name、Pythonf"{name}"或各类代码生成模板语法的人)在未打标签(untagged)的模板字符串中写出{name}$name}时,它并不会被当作插值执行,而只是普通文本——这可能是一个静默的 bug。

该规则的官方描述是 "Disallow incorrect template literal interpolation syntax."(禁止不正确的模板字面量插值语法),元数据中标记为type: 'problem'recommended: true(因此包含在 ✅recommended配置中,而在 ☑️unopinionated配置中禁用),并通过editor suggestions提供手动可应用的修复(hasSuggestions: true)。相关声明见 rules/no-incorrect-template-string-interpolation.js。

规则刻意保持"窄":只抓最确定的错误

规则文档明确指出其检测范围是刻意收窄的(见 docs/rules/no-incorrect-template-string-interpolation.md):

  • 只匹配简单标识符或成员表达式:如{name}{user.name}$user.name}
  • 不报告任意表达式文本:如{name + suffix}{user?.name}{user[name]}
  • 不报告{{name}}这类花括号占位符语法
  • 忽略带标签的模板字符串(tagged template literal),因为标签(如htmlgql、i18n 模板)中常使用花括号表示其他语言语法;
  • 忽略命名导入/导出说明符import {foo} from "bar")、解构声明(const {foo} = bar),以及内嵌块注释(如 JSDoc 的@param {string})中的花括号——这些在代码生成模板中都很常见。

同时,对于确实有意使用简单花括号占位符(如 URL 路径/users/{id})或包含对象字面量(`const x = {foo}`)的文件,官方建议直接禁用此规则。

源码实现:两条正则识别两类插值错误

规则源码 rules/no-incorrect-template-string-interpolation.js 中定义了两个正则来识别两类错误:

const identifier = String.raw`[$A-Z_a-z][\w$]*`; const memberExpression = String.raw`${identifier}(?:\.${identifier})*`; const missingDollar = new RegExp(String.raw`(?<![$\\])(?<!\{)(?<!\\u)\{(?<expression>${memberExpression})\}(?!\})`, 'gv'); const missingOpeningBrace = new RegExp(String.raw`(?<!\\)(?<!\{)\$(?<expression>${memberExpression})\}(?!\})`, 'gv');
  • missingDollar(缺$:匹配{name}{user.name}这种缺了$的形式。前瞻断言(?<!\{)(?!\})让它跳过{{name}}(?<![$\\])避免误伤已转义文本;(?<!\\u)用于排除\u{FEFF}这类 Unicode 码点转义。
  • missingOpeningBrace(缺{:匹配$name}$user.name}这种少了左花括号的形式,修复时补全为${name}

规则遍历TemplateLiteral节点的每个 quasi(文本片段),对每一处命中生成一个独立的 lint 报告与修复建议。检测前的关键预处理包括:

  • 跳过带标签模板:调用 rules/ast/is-tagged-template-literal.js 判断节点是否为 TaggedTemplateExpression 的 quasi(对应 AST 助手 rules/ast/index.js),是则直接返回;
  • 屏蔽已解析的表达式区域getTemplateRawWithExpressionsMasked${...}表达式内容替换为空格,避免把表达式内部的花括号误判为占位符;
  • 排除绑定声明上下文isBindingDeclaration只对import/export/const/let/var行首的{foo}放行(详见下文"排除项");
  • 排除块注释区域getBlockCommentRanges通过/\*[\s\S]*?\*\//g找到所有已闭合/* … */注释范围,命中范围内的花括号一律不报。

快照测试解读:21 个错误用例的行为全览

快照报告由 AVA 测试框架生成(Generated by AVA),文件头部指明实际快照保存在no-incorrect-template-string-interpolation.js.snap,而测试定义位于 test/no-incorrect-template-string-interpolation.js。每条用例输出统一的消息格式:

Use `${correct}` for template literal interpolation.

并附带一条建议(Suggestion):

Replace `{incorrect}` with `${correct}`.

下面按错误形态分类解读快照中的 21 个用例。

1. 最简单的{name}$错误

  • invalid(1):const greeting = \Hello {name}`;→ 报错并建议改为Hello ${name}`;
  • invalid(7):Hello {user.name}→ 支持成员表达式,建议改为${user.name}
  • invalid(13)/invalid(14):${salutation}, {name}${salutation}, {name} ${punctuation}→ 证明规则不会把${...}中的表达式误判,只精确指向缺少${name}

2.$name}{的错误

  • invalid(8):Hello $name}→ 建议改为Hello ${name}
  • invalid(9):Hello $user.name}→ 同样支持成员表达式,改为${user.name}

3. 同一模板中的多处错误:逐个独立报告

  • invalid(10):Hello {firstName} {lastName}→ 产生Error 1/2 与 Error 2/2 两条独立报告,每条建议分别把对应占位符修正为${firstName}${lastName}
  • invalid(11):Hello $firstName} $lastName}→ 同样是两条报告,各自修复;
  • invalid(12):Hello {firstName} ${middleName} {lastName}→ 中间的${middleName}完全不受影响,仅报告两侧的两处错误。

这印证了源码中yield逐处产出报告的实现方式(rules/no-incorrect-template-string-interpolation.js),每处错误都有独立的行列位置与修复建议。

4. 绑定声明保护:只放过真正的 specifier/解构

规则通过isBindingDeclaration(rules/no-incorrect-template-string-interpolation.js)识别"该行是绑定声明"的场景,避免把代码生成模板中的合法语法误报:

  • invalid(2):`const x = {name};`仍然报告。因为声明要求{foo}出现在行首(=之前),{name}位于=之后属于对象字面量,会被判为疑似插值错误;
  • invalid(3):Please import {name} now→ 报告。import出现在句中而不是行首,不属于说明符;
  • invalid(4):important {name}→ 报告。important不是关键字import
  • invalid(5):constant {name}→ 报告。同理constant不是const
  • invalid(6):const $foo} = bar;→ 报告并建议${foo}。测试注释明确指出:isBindingDeclaration只保护{foo}形态,不保护缺左花括号的$foo}形态

5. 块注释边界:只放行已闭合的注释

  • invalid(15):`/** ${description}\n*/ {name}`→ 报告。注释在*/处已闭合,闭合后的{name}是真实错误;
  • invalid(16):`/* doc */ Hello {name}`→ 报告。同理,闭合注释不抑制其后的错误;
  • invalid(17):`{name} /* {type} */`→ 报告。注释之前的真实错误照常报出,注释内部的{type}被忽略——两条行为同时发生;
  • invalid(18):`const re = "/*"; {name}`→ 报告。字符串里的/*没有闭合的*/不构成块注释(源码注释明确说明只有/* … */成对闭合才计数),因此不抑制错误;
  • invalid(19):`// {name}`→ 报告。行注释刻意不支持//后的{name}仍被判定为错误;
  • invalid(20):`\u{FEFF}{name}`→ 报告。Unicode 码点转义\u{FEFF}本身被排除,但紧邻其后的真实错误{name}仍然报告,建议改为\u{FEFF}${name}

对照 valid 用例:理解规则的排除清单

快照只覆盖 invalid 输出,而规则完整行为需要结合测试文件中的 valid 用例理解(test/no-incorrect-template-string-interpolation.js):

场景示例不报原因
正确插值`Hello ${name}``Hello ${user.name}`语法正确
转义文本`Hello \${name}``Hello \{name}`反斜杠转义被前瞻断言排除
带标签模板html`{name}`gql`query { user { name } }`String.raw`{name}`标签常用于 HTML/GraphQL 等语言
非模板字符串"{name}""$name}"规则只处理 TemplateLiteral 节点
复杂表达式`Hello {name + suffix}``Hello {user?.name}``Hello {user[name]}`只匹配简单标识符/成员表达式
双花括号`Hello {{name}}``Hello {{$name}}`(?!\})/(?<!\{)断言
导入/导出说明符`import {foo} from "bar";``export {foo};``import foo, {bar} from "baz";`isBindingDeclaration行首保护
解构声明`const {foo} = bar;``let {foo} = bar;`同上
JSDoc/块注释`/**\n * @param {string} [id]\n */``/* {number} */``/* {foo.Bar} */`getBlockCommentRanges排除
注释与插值混合`/** ${description} @param {string} id */`表达式被屏蔽后注释仍被识别
Unicode 转义`\u{FEFF}${csv}``\u{Face}`(?<!\\u)排除码点转义

从 valid 用例还能看出两个重要的实现细节:import type {foo} from "bar"(TypeScript 代码生成)和缩进的多行 import(`\n\timport {foo} from "bar";\n`)都被正确放行;多说明符import {foo, bar}因含逗号根本不进入匹配范围。

运行测试与验证行为

测试通过test.snapshot(...)组织(test/no-incorrect-template-string-interpolation.js),使用项目统一的快照测试器(基于eslint-ava-rule-tester,见 test/utils/test.js)。若需在本地复现快照中的输出,可运行:

# 运行全部测试 npm test # 仅运行该规则相关测试 npx ava test/no-incorrect-template-string-interpolation.js # 快照不匹配时更新快照(注意:仓库只读,更新结果仅用于本地观察) npx ava test/no-incorrect-template-string-interpolation.js --update-snapshots

规则已在 rules/index.js 中注册为no-incorrect-template-string-interpolation,可通过标准 ESLint 配置启用,例如:

{ "rules": { "unicorn/no-incorrect-template-string-interpolation": "error" } }

由于规则通过编辑器建议(而非fix)提供修复,IDE 中会显示"Replace{name}with${name}"的可手动应用修复;命令行下则需配合--fix-type suggestion语义或手动接受建议。

实际项目中的应用建议

  • 确认占位符语法:若项目大量使用/users/{id}这类 URL 模板或代码生成模板,评估后可在相关文件顶部用/* eslint-disable unicorn/no-incorrect-template-string-interpolation */局部禁用;
  • 善用标签模板:对 HTML、GraphQL、CSS 等内嵌语言,优先使用带标签的模板字符串,规则天然放行;
  • 注意行注释与未闭合注释:规则只识别成对的/* … */`// {name}`与字符串中的孤立/*仍会被报告,编写代码生成模板时应使用规范注释;
  • 消息与修复的自动化集成:错误消息与建议文本格式稳定(Use \${correct}` for template literal interpolation./Replace `{incorrect}` with `${correct}`.),可接入自定义工具做批量迁移(例如把旧模板语法批量改写为${}`),但批量改写前应结合 valid 用例清单复核排除场景。

总结

no-incorrect-template-string-interpolation通过"两条正则 + 三层排除(绑定声明、块注释、表达式屏蔽)"实现了对模板字符串插值手误的精确检测。快照测试报告完整展示了其 21 个错误用例的行为:单标识符与成员表达式都支持、多处错误逐处独立报告、绑定声明保护与块注释边界处理细致入微。理解这些边界行为,能帮助你在启用规则时准确预判误报风险,并让代码生成模板类项目也能安全受益。

【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询