I'd like to create a function that can be called normally:
myFn(arg1, arg2)
Or as a tagged template literal:
myFn`text ${someVar}`
In the implementation of myFn
, is it possible to detect whether it was called normally or as a tagged template literal? The arguments passed to a template literal have a certain pattern (first argument is an array of strings, additional arguments, if any exist, will be one fewer than the length of the array in the first argument), so I could detect based off that. But someone could theoretically pass in the same argument pattern to the normal function call.
Is there any special way to detect the way it was called beyond argument pattern detection?
I don't think there is any way to check with absolute certainty, but you can add some things to your argument checks to make it less likely that it is accidentally called with arguments that satisfy your checks. If called as a template tag, the first argument will be an array with at least one element, it will be frozen, and it will have a property called raw
that is an array of the same length. You can also check the number of remaining arguments, as you already mentioned:
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`${0} ${1}`;
myFn``;
// isTag === false
myFn( );
myFn( ['test'] );
You could go a bit further by verifying that the elements in the array and in raw
are all strings and even check that they are the same strings after replacing escape sequences in the raw strings with the appropriate characters. I think that is probably unnecessary, because it won't make it any more difficult for someone to purposefully trick your function (although it will limit the inputs they can use to trick it), and it is already unlikely that the above test fails unless someone is attempting to purposefully trick it.