I've read a great pattern of handling required function parameters on 2ality.
function throwIfMissing() {
throw new Error('Missing parameter');
}
function foo(mustBeProvided = throwIfMissing()) {
return mustBeProvided;
}
Is there any such nice and clean way to throw validation errors?
function throwIfInvalid(value) {
const max = 10;
const min = 5;
if(value < min || value > max){
throw new Error(`Value must be between ${min} ${max}`);
}
return value;
}
function foo(mustBeValid = throwIfInvalid()) {
return mustBeValid;
}
Of course the throwIfInvalid()
function does not work as expected. My question is if there is any trick to make it work.?
I've read a great pattern of handling required function parameters on 2ality.
function throwIfMissing() {
throw new Error('Missing parameter');
}
function foo(mustBeProvided = throwIfMissing()) {
return mustBeProvided;
}
Is there any such nice and clean way to throw validation errors?
function throwIfInvalid(value) {
const max = 10;
const min = 5;
if(value < min || value > max){
throw new Error(`Value must be between ${min} ${max}`);
}
return value;
}
function foo(mustBeValid = throwIfInvalid()) {
return mustBeValid;
}
Of course the throwIfInvalid()
function does not work as expected. My question is if there is any trick to make it work.?
- 2 why doesnt that code work? – Naeem Shaikh Commented Jul 6, 2017 at 9:55
- it seems perfectly fine to me, your question is not for stackoverflow but for the ments section in the website link you're provided.. – vsync Commented Jul 6, 2017 at 9:58
-
if your
foo
function expects some argument and it is missing, then your code will fail unless you are able to verify the parameter is as expected – vsync Commented Jul 6, 2017 at 10:00 -
Instead of abusing default parameters I would encourage you to use a
Proxy
along with an apply trap to achieve this kind of runtime type checks. – user6445533 Commented Jul 6, 2017 at 10:43
3 Answers
Reset to default 5Of course it doesnt work, i think you are misunderstanding how Default Parameters work.
When you write
function foo(mustBeProvided = throwIfMissing())
you are assigning the result of throwIfMissing
to the parameter IF AND ONLY IF the parameter is undefined
.
Meaning that if that parameter is provided then throwIfMissing
is not called at all; it will never execute and will never check for the validity of your parameter.
That's why you can only use this "trick" to react to an undefined parameter, not to validate one.
--
Alternative Solution
Personally i would solve the problem with a simple solution
function checkRange(val, min, max) {
if (val > max || val < min) {
throw new Error();
}
}
function foo(val) {
checkRange(val, 5, 10);
return val;
}
You may argue that this is much less elegant but keep in mind that in this case you can reuse the validator with different values, you don't have to hardcode min and max.
If you are looking for an npm module you can try this one but i wouldn't go that far for a simple validation.
This trick is only good for a generalized error message, and cannot be forced to be specific to the function it was checking its parameters, since the parameters were never supplied in the first place.
Now, a lesson:
Consider this situation:
function missingArgument() {
console.warn('Missing parameter');
}
function foo(bar = missingArgument()){
return bar.value;
}
console.log( foo() ) // will throw an error since "bar" is not a value
console.log('end'); // never reach here
The above example will throw an error, but the trick with the missingArgument
function isn't very helpful since it doesn't tells you imminently where was the problem at. You can pass a string which will give helpful information:
function missingArgument(name) {
console.warn('Missing parameter in function:', name);
}
function foo(bar = missingArgument('foo')) {
return bar.value;
}
console.log(foo()) // will throw an error since "bar" is not a value
console.log('end'); // never reach here
This can be solved in this manner:
function missingArgument() {
console.warn('Missing parameter');
}
function foo(bar = missingArgument()){
if( bar == undefined ) return 0;
return bar.value;
}
console.log( foo() )
console.log('end');
Each function obviously does its own thing, so you must know what the function should do with the expected argument and what is the expected output of the function, and then tailor-make your protection at the beginning of it.
The foo
function above is expecting an Object and then it tries to access the value
key of that object, and since the parameter is undefined
it will fail and your whole code chain will stop. This is mostly unwanted and can be avoided.
Above example will only work if the Object has a value
key, like so:
console.log( foo({value:1}) )
As the other answers have pointed out, in the example provided by you, the throwIfInvalid()
will get triggered only when the argument has not been passed. So you can not use it the way you did in the next example.
However, if you must use something similar, I could only think of the following :
function isValidNumber(value, min = 5, max = 10) {
if(isNaN(value) || value < min || value > max) {
throw new Error(`Value must be a number between ${min} ${max}`);
}
return true;
}
function foo(mustBeValid, isValid = isValidNumber(mustBeValid)) {
return mustBeValid;
}
try {
// This will return '7'
var bar = foo(7);
// Showing the value in the screen
document.write(bar);
// This will throw an error
bar = foo(1);
} catch (e) {
document.write('<br><br>'+e);
}
However, I would rather just call a validation method at the start as a guard clause. Or you can also checkout Proxy
:
https://developer.mozilla/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#Validation