Don't allow template literal to contain variable - javascript

I wish to create a function that takes an argument as a template literal, however will throw an error if the template literal has any variables within the template.
For example the following is valid.
const value = checker(`hello world`)
However this would throw an error.
const value = checker(`hello ${name}`)
What's the best way to achieve this?

Template literals allow for tagging, it can be achieved like this:
var a = 5;
var b = 10;
function noVarTemplate(strings, ...values) {
if (values.length) throw new Error('the noVarTemplate does not allow template literal values')
return strings[0]
}
const value = noVarTemplate`Hello World`
// const value = noVarTemplate`Hello ${a}`
console.log(value)

Related

How to use an expression in the Object literal key?

I have a switch^
let s = "aaa"
switch (true) {
case s.includes('a'):
url = 'https://a.com/wp-admin/post-new.php'
console.log(url)
break
case s.includes('b'):
url = 'https://b.com/wp-admin/post-new.php'
console.log(url)
break
default:
console.log('default')
break
}
I'm trying to change this to a literal object, but I can't figure out how to properly specify the s.includes condition as the object key
const objLit = (s) => {
const cases = {
//s.includes('a'): 'https://a.com/wp-admin/post-new.php',
[`${s.includes('a')}`]: 'https://a.com/wp-admin/post-new.php',
//s.includes('b'): 'https://b.com/wp-admin/post-new.php',
[`${s.includes('b')}`]: 'https://b.com/wp-admin/post-new.php',
}
let url = cases[s]
return url
}
console.log(objLit(s))
Although this does not throw an error, it does not work.
You can't. Property names are strings or Symbols.
When you use [] syntax, the expression is evaluated at object creation time.
You can't assign code that gets evaluated at lookup time. This is what switch and if/else are for.
In your case, since includes() returns a boolean, you are just creating an object with properties named "true" and "false".
You could say let url = cases.true, but it would be a horrible hack.

How to create a tagged template literal and pass argument to another tagged template literal?

Let's say I have
const highlight = (strings, ...values) => {
// console.logs in highlight
}
Let's say I want to create a "middleware" that modifies the template and then call highlight instead:
const doMore = (string, ...values) => {
// modify string/values and then call highlight here
}
So that I can now do something like
doMore`
the string ${template}
`;
I cannot figure out how to call highlight from doMore. How do I do this?
My first attempt was to use the ... operator. But this did not work. Specifically, I'm trying to create a wrapper for the npm chalk application, so I did something like:
const doMore = (string, ...values) => {
// extend string, values
return chalk(string, ...values)
}
but this throws an error: chalk_1.default.apply is not a function. Normally doing
chalk`the string ${template}`
but calling it using the spread operator is throwing this error.
You can use the ... spread syntax to convert an array into a list of arguments to call highlight:
const doMore = (string, ...values) => {
string += " foo";
values = values.map(v => v + " bar");
return highlight(string, ...values);
};
It looks like chalk_1.default.apply is an error from the transpiler that you are using rather than your code.
Here is an example of modifying the output from the tagged template literal.
const doMore = (template, ...values) => {
/**
* Make sure the template array is the same length as template.raw
*/
/** Add START on the first line. */
template[0] = "START" + template[0];
template.raw[0] = "START" + template.raw[0];
/** Add END on the last line. */
template[template.length - 1] =
template[template.length - 1] + "\nEND";
template.raw[template.raw.length - 1] =
template.raw[template.raw.length - 1] + "\\nEND";
return chalk(template, values);
};
console.log(doMore`
{red This text is shown in red.}
${"This text is inserted."}
{green This text is shown in green.}`);
Outputs:
START
This text is shown in red.
This text is inserted.
This text is shown in green.
END

How to split ``(Backtick string) on each instance of ${variable}

So my question is how i can split the a string with backtick on each instance of variable.
I tried with \${.*?} but this will not work because ${variable} will be replaced by variable values first and than the split function will be executed.
Any idea how to do it ?
let a = 2
let b = 4
let x = `Superman${a}hello${b}one more`.split(/\${.*?}/g)
console.log(x)
On side not: I don't want a solution with wrapping it to " or '.
console.log('`Superman${a}hello${b}one more`'.split(/\${.*?}/g))
After the line executes, there is no way to get the original template string. However, you can use a tag function/tagged template literal to get the parts of the string, including the substitution values:
function Test() {
console.log(arguments)
return arguments.length - 1
}
let a = 2
let b = 4
let c = Test `Superman${a}hello${b}one more`
console.log(`This template string has ${c} substituted values`)
To clarify my comment to the original question here is an example of what the default Template Literal Function does:
function defaultTemplateLiteralFn(strs, ...args) {
return strs.map((str, idx) => str+(args[idx]||'')).join('');
}
const test = "special test";
const a = 10;
const b = 432;
console.log(`This is a ${test}. "${a}+${b}=${a+b}"`)
console.log(defaultTemplateLiteralFn`This is a ${test}. "${a}+${b}=${a+b}"`)
When you use a tagged template (IE: You don't provide a function to handle the template literal) The the language provides a default function that does something similar to what I do in my function defaultTemplateLiteralFn above. It returns the combined parts of the string with the values.
The example function takes each part of the string and puts the appropriate value after the string. If there is no value then it uses a blank string.
One way i have done is using template literal. i have seen this is being used in a in a library called styled-components which allows us to write css with js.
Would love to see other methods if there are any ?
function splitOnVariable(str, age){
// first argument to function will always be array of strings provided in input string.
return str
}
let a = 1;
let b = 2;
console.log(splitOnVariable`hello${a} break me on variable${b} !!!`)

Delayed evaluation for string formatting

I am building up a constants.js file with URLs used in my ReactJS project. These strings include query parameters that can be used with the URL. However, the values used in these strings are only available in a component where the string is used, not in the constants file itself. So for example, I want something like this:
export const BASE_URL = 'https://example.com';
export const FOO_QUERY = '?foo=%s';
where %s is just a placeholder that can be replaced later in a component. (I borrowed printf() syntax from C here for illustration purposes.) What is the correct syntax to do something like this in JavaScript? Is it even possible?
I'd probably use a template literal in a function: The component calls the function with the foo value:
export const fooQuery = foo => `?foo=${foo}`;
Usage:
const query = fooQuery("foo value");
With tagged template literals you could do:
function query(parts, ...pos) {
return apply(...args) {
return parts.map((part, i) => part + args[ pos[i] ]).join("");
}
}
Usable as:
const find = query`?name=${0}&fullname=${0}&age=${1}`;
console.log(find("jonas", 18));
let string = 'bar';
console.log("Foo %s", string);
var teststr = (string) => `Foo ${string}`; //result: Foo bar

Determine original name of variable after its passed to a function

I've got a feeling this might not be possible, but I would like to determine the original variable name of a variable which has been passed to a function in javascript. I don't know how to explain it any better than that, so see if this example makes sense.
function getVariableName(unknownVariable){
return unknownVariable.originalName;
}
getVariableName(foo); //returns string "foo";
getVariableName(bar); //returns string "bar";
This is for a jquery plugin i'm working on, and i would like to be able to display the name of the variable which is passed to a "debug" function.
You're right, this is very much impossible in any sane way, since only the value gets passed into the function.
This is now somehow possible thanks to ES6:
function getVariableName(unknownVariableInAHash){
return Object.keys(unknownVariableInAHash)[0]
}
const foo = 42
const bar = 'baz'
console.log(getVariableName({foo})) //returns string "foo"
console.log(getVariableName({bar})) //returns string "bar"
The only (small) catch is that you have to wrap your unknown variable between {}, which is no big deal.
As you want debugging (show name of var and value of var),
I've been looking for it too, and just want to share my finding.
It is not by retrieving the name of the var from the var but the other way around : retrieve the value of the var from the name (as string) of the var.
It is possible to do it without eval, and with very simple code, at the condition you pass your var into the function with quotes around it, and you declare the variable globally :
foo = 'bar';
debug('foo');
function debug(Variable) {
var Value = this[Variable]; // in that occurrence, it is equivalent to
// this['foo'] which is the syntax to call the global variable foo
console.log(Variable + " is " + Value); // print "foo is bar"
}
Well, all the global variables are properties of global object (this or window), aren't they?
So when I wanted to find out the name of my variables, I made following function:
var getName = function(variable) {
for (var prop in window) {
if (variable === window[prop]) {
return prop;
}
}
}
var helloWorld = "Hello World!";
console.log(getName(helloWorld)); // "helloWorld"
Sometimes doesn't work, for example, if 2 strings are created without new operator and have the same value.
Global w/string method
Here is a technique that you can use to keep the name and the value of the variable.
// Set up a global variable called g
var g = {};
// All other variables should be defined as properties of this global object
g.foo = 'hello';
g.bar = 'world';
// Setup function
function doStuff(str) {
if (str in g) {
var name = str;
var value = g[str];
// Do stuff with the variable name and the variable value here
// For this example, simply print to console
console.log(name, value);
} else {
console.error('Oh snap! That variable does not exist!');
}
}
// Call the function
doStuff('foo'); // log: foo hello
doStuff('bar'); // log: bar world
doStuff('fakeVariable'); // error: Oh snap! That variable does not exist!
This is effectively creating a dictionary that maps variable names to their value. This probably won't work for your existing code without refactoring every variable. But using this style, you can achieve a solution for this type of problem.
ES6 object method
In ES6/ES2015, you are able to initialize an object with name and value which can almost achieve what you are trying to do.
function getVariableName(unknownVariable) {
return Object.keys(unknownVariable)[0];
}
var foo = 'hello';
var output = getVariableName({ foo }); // Note the curly brackets
console.log(output);
This works because you created a new object with key foo and value the same as the variable foo, in this case hello. Then our helper method gets the first key as a string.
Credit goes to this tweet.
Converting a set of unique variable into one JSON object for which I wrote this function
function makeJSON(){ //Pass the variable names as string parameters [not by reference]
ret={};
for(i=0; i<arguments.length; i++){
eval("ret."+arguments[i]+"="+arguments[i]);
}
return ret;
}
Example:
a=b=c=3;
console.log(makeJSON('a','b','c'));
Perhaps this is the reason for this query
I think you can use
getVariableName({foo});
Use a 2D reference array with .filter()
Note: I now feel that #Offermo's answer above is the best one to use. Leaving up my answer for reference, though I mostly wouldn't recommend using it.
Here is what I came up with independently, which requires explicit declaration of variable names and only works with unique values. (But will work if those two conditions are met.)
// Initialize some variables
let var1 = "stick"
let var2 = "goo"
let var3 = "hello"
let var4 = "asdf"
// Create a 2D array of variable names
const varNames = [
[var1, "var1"],
[var2, "var2"],
[var3, "var3"]
]
// Return either name of variable or `undefined` if no match
const getName = v => varNames.filter(name => name[0] === v).length
? varNames.filter(name => name[0] === v)[0][1]
: undefined
// Use `getName` with OP's original function
function getVariableName(unknownVariable){
return getName(unknownVariable)
}
This is my take for logging the name of an input and its value at the same time:
function logVariableAndName(unknownVariable) {
const variableName = Object.keys(unknownVariable)[0];
const value = unknownVariable[variableName];
console.log(variableName);
console.log(value);
}
Then you can use it like logVariableAndName({ someVariable })

Categories