There are excellent answers already to make stringLengths
work with any number of parameters. However, all (so far) assume that each parameter is a string.
Which can lead to unexpected results:
- Both arrays and functions have a
length
property
- Any objects can be crafted with a
length
property
Or even errors:
- Things like
null
or undefined
will throw an error when attempting to read properties from them.
const stringLengths = (...strs) =>
strs.map(s => s.length);
console.log(
stringLengths(1, 2)
);
console.log(
stringLengths({length: ''}, [''], (a, b) => '')
);
console.log(
stringLengths(null, null)
);
You can write a decorator filterStrings
which you can apply to stringLengths
so that it gets only strings:
const stringLengths = (...strs) =>
strs.map(s => s.length);
const filterStrings = fn => (...args) =>
fn(...args.filter(arg => typeof arg === 'string'));
const safeStringLengths = filterStrings(stringLengths);
console.log(
safeStringLengths(1, 2)
);
console.log(
safeStringLengths({length: ''}, [''], (a, b) => '')
);
console.log(
safeStringLengths(null, null)
);
console.log(
safeStringLengths("foo", "bar", "foobar")
);
filterStrings
is the function that actually gets all the parameters, it keeps only strings and pass them on to stringLengths
.
Why do it this way?
It depends on your context really. The nice thing about the decorator is that it allows you to move the validation of the parameters outside of stringLengths
and let the function do just one thing.
In some scenarios you can simply trust your parameters and having validation in your function would be useless. In other scenarios, you can't. Being able to combine functions allows you to keep things separated and organised.