如何检测函数是被正常调用还是被标记为模板文字?

How to detect whether a function was called normally or as a tagged template literal?

我想创建一个可以正常调用的函数:

myFn(arg1, arg2)

或作为标记的模板文字:

myFn`text ${someVar}`

myFn的实现中,是否可以检测是正常调用还是标记模板字面量调用?传递给模板文字的参数有一定的模式(第一个参数是一个字符串数组,其他参数,如果有的话,将比第一个参数中的数组长度少一个),所以我可以根据它进行检测.但理论上有人可以将相同的参数模式传递给正常的函数调用。

除了参数模式检测之外,是否有任何特殊方法来检测它的调用方式?

我认为没有任何方法可以绝对确定地进行检查,但您可以在参数检查中添加一些内容,以减少使用满足您检查的参数意外调用它的可能性。如果作为模板标签调用,第一个参数将是一个至少包含一个元素的数组,它将被冻结,并且它将有一个名为 raw 的 属性 ,它是一个相同长度的数组。您还可以检查剩余参数的数量,正如您已经提到的:

function myFn ( arg1, ...rest ) {
  const isTag = !!(
    arg1 && arg1.length > 0 && arg1.raw && arg1.raw.length === arg1.length &&
    Object.isFrozen( arg1 ) &&
    rest.length + 1 === arg1.length
  );

  console.log( isTag );
}

// isTag === true
myFn`test`;
myFn`[=10=] `;
myFn``;

// isTag === false
myFn( );
myFn( ['test'] );

您可以更进一步,验证数组和 raw 中的元素都是字符串,甚至在用适当的字符替换原始字符串中的转义序列后检查它们是否是相同的字符串.我认为这可能是不必要的,因为它不会让有人故意欺骗你的功能变得更加困难(尽管它会限制他们可以用来欺骗它的输入),并且上面的测试已经不太可能失败,除非有人试图故意欺骗它。

这是一个用 Typescript 编写的简短助手

export function is_tagged(a: any[]): a is [TemplateStringsArray, ...any[]] { // @ts-expect-error
    return a[0] instanceof Array && a[0].raw instanceof Array && a[0].length === a.length;
}

用法:

function foo(...args) {
    if (is_tagged(args)) {
        console.log("tagged!")
    } else {
        console.log("normal")
    }
}

foo("bar"); // "normal"
foo`bar`; // "tagged!"