String.dedent S2
- 阶段: Stage 2
- 状态: 进行中
- ECMAScript 版本: —
- 同步时间: 2026年8月26日
- English original · 官方仓库
该提案引入了 String.dedent 标签函数用于模板字面量,以自动移除公共缩进,解决嵌入多行文本时的可读性问题。它指定了处理开头/结尾行和仅空白行的规则,以及与其他标签函数的组合方式。
以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。
String dedent
提案发起人: @jridgewell, @hemanth
作者: @mmkal
状态: Stage 2
问题
当尝试在 JS 代码中嵌入格式化文本(例如,Markdown 内容或 JS 程序的源代码)时,开发者不得不为了代码或输出的可读性做出尴尬的让步。例如,为了使嵌入的文本与周围代码看起来一致,我们会编写:
这会输出(使用 ^ 标记行首,· 标记前导空格):
为了使输出看起来合理,我们的代码变得难以阅读:
这输出了一个合理的输出:
使用库
借助库可以写出合理的代码并获得合理的输出。
这输出了合理的:
然而,这些库会产生运行时成本,并且在执行“去缩进”的方式上存在微妙的不一致。最流行的包停滞不前,没有错误修复,并且对模板对象的 .raw 数组的解析存在问题,而且没有任何库能够将去缩进的文本传递给标签模板函数。
此外,即使一个用户态库支持传递给标签模板,在像 Array.isTemplateObject 这样的提案中,该数组也不是真正的模板对象。这会损害标签模板函数区分实际程序源代码中存在的去缩进模板(并赋予更高的信任级别)与动态生成的字符串(可能包含用户生成的利用字符串)的能力。
提议的解决方案
实现一个 String.dedent 标签模板函数,用于标签模板字面量,其行为几乎与常规的单反引号模板字面量相同,但有几个关键区别:
- 开头行(紧挨着开头
``右侧的所有内容)必须只包含一个字面换行符。 - 开头行的字面换行符被移除。
- 结尾行(紧挨着结尾
``左侧的所有内容)可以包含空白字符,但空白字符会被移除。 - 结尾行之前的字面换行符被移除。
- 仅包含空白字符的行被清空。
- 计算所有非空内容行(不是开头或结尾行的行)的“公共缩进”。
- 从每一行的开头移除该公共缩进。
可以尝试使用 REPL 实现来体验。
上述示例可以这样解决:
这输出了合理的:
表达式可以直接支持,也可以与另一个标签模板函数组合:
在其他语言中
- CoffeeScript - 块字符串 使用
'''和"""三引号。 - Java - 文本块 使用三引号。
- Kotlin - 原始字符串 使用三引号和
.trimIndent()。 - Scala - 多行字符串
使用三引号和
.stripMargin。 - Python - 多行字符串 使用三引号
来避免转义以及
textwrap.dedent。 - Jsonnet - 文本块 使用
|||作为分隔符。 - Bash -
<<-Heredocs。 - Ruby -
<<~Heredocs。 - Swift - 多行字符串字面量 使用三引号 - 根据结束定界符之前的空白去除边距。
- PHP -
<<<heredoc/nowdoc。 结束标记的缩进决定了每行要剥离的空白量。
问答
为什么不使用库?
总结上面的问题部分:
- 避免对绝大多数多行字符串所需的行为产生依赖(dedent 每周有数百万次下载)。
- 避免多个当前实现之间的不一致。
- 提高性能。
- 更好的可发现性 - 该功能可以公开记录,并在代码示例中使用,而无需依赖像 dedent 这样的包。
- 为代码生成器提供一种输出具有正确缩进属性的可读代码的方式(例如,jest 内联快照)。
- 支持具有定制表达式参数行为的“去缩进”标签模板字面量函数(例如 slonik)。
- 允许格式化器/检查器安全地强制代码风格,而不需要与多个库的运行时行为相结合。