You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Handlebars自定义eq助手在else语句中调用出现值未定义问题

解决Handlebars自定义eq助手在else分支中变量丢失的问题

问题重现

我用TypeScript实现了Handlebars的eq自定义助手,代码如下:

const handleOptions = (
  handlebarsInstance: typeof handlebars,
  result: boolean | string,
  options: HandlebarsBlockOptions<typeof handlebars>,
): boolean | string => {
  if (options.fn && options.inverse) {
    // The helper is being used as a block helper
    return result
      ? options.fn(handlebarsInstance)
      : options.inverse(handlebarsInstance)
  }

  // The helper is being used in a sub-expression
  return result
}

function equality(
  this: typeof handlebars,
  arg1: unknown,
  arg2: unknown,
  options: HandlebarsBlockOptions<typeof handlebars>,
): boolean | string {
  logger.info('"equality" helpers triggered', {
    arg1,
    arg2,
    options,
  })

  return handleOptions(this, arg1 == arg2, options)
}

export const registerEqualityHelper = (
  handlebarsInstance: typeof handlebars,
) => {
  handlebarsInstance.registerHelper('eq', equality.bind(handlebarsInstance))
}

// 注册助手
const handlebarInstance = handlebars.create()
registerEqualityHelper(handlebarInstance)

该助手在多数场景正常,但使用如下模板时出现问题:

{{#eq var1 "foo"}}Text 1{{else eq var1 "bar"}}Text 2{{else}}Text 3{{/eq}}

当var1等于"bar"时,预期输出Text 2,实际输出Text 3。排查发现此时equality函数的arg1为undefined,导致undefined == "bar"判断为false,且仅在else分支调用助手时出现该问题。

尝试过用../var1或@root.var1指定变量,虽有效但不够友好;改用子表达式嵌套if的方式({{#if (eq var1 "foo")}}Text 1{{else if (eq var1 "bar")}}Text 2{{else}}Text 3{{/if}})能解决,但需要修改模板,不符合需求。

问题根源

  1. 错误绑定了助手的上下文:注册助手时用equality.bind(handlebarsInstance)把this绑定成了Handlebars实例,而Handlebars块助手的this原本应该指向当前模板的数据上下文。
  2. 传递错误的上下文给分支函数:handleOptions中调用options.fn和options.inverse时,传入的是Handlebars实例而非数据上下文,导致else分支里的变量无法从正确的上下文中获取,最终arg1变成undefined。

修复方案

不需要修改模板,只需调整助手的实现逻辑,确保正确传递数据上下文:

1. 修改handleOptions函数

将第一个参数改为数据上下文,调用分支函数时传入该上下文:

const handleOptions = (
  context: unknown,
  result: boolean | string,
  options: HandlebarsBlockOptions<typeof handlebars>,
): boolean | string => {
  if (options.fn && options.inverse) {
    // 传递数据上下文给块分支
    return result
      ? options.fn(context)
      : options.inverse(context)
  }

  // 子表达式场景直接返回结果
  return result
}

2. 调整equality函数的上下文类型

将this的类型改为数据上下文(unknown),并将其传递给handleOptions:

function equality(
  this: unknown, // 指向模板的数据上下文
  arg1: unknown,
  arg2: unknown,
  options: HandlebarsBlockOptions<typeof handlebars>,
): boolean | string {
  logger.info('"equality" helpers triggered', {
    arg1,
    arg2,
    options,
  })

  return handleOptions(this, arg1 == arg2, options)
}

3. 修正助手注册方式

移除bind(handlebarsInstance),让Handlebars自动将数据上下文绑定到助手的this:

export const registerEqualityHelper = (
  handlebarsInstance: typeof handlebars,
) => {
  handlebarsInstance.registerHelper('eq', equality)
}

验证效果

修改后重新运行模板{{#eq var1 "foo"}}Text 1{{else eq var1 "bar"}}Text 2{{else}}Text 3{{/eq}},当var1为"bar"时,第一个eq判断为false进入else分支,此时分支内的eq助手能正确获取var1的值,判断"bar" == "bar"为true,输出预期的Text 2。

内容的提问来源于stack exchange,提问作者Guillaume C.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 14:10:53