Often I use objects to pass around a set of options.
In 99.9% of these cases I expect these objects to only contain a subset of the property I use. If an unexpected property is present, it means almost always that there's a typo or a logical error.
Is there a simple way to make sure that the option objects don't contain unexpected properties? I would only really need this during testing and debugging, so it doesn't have to be efficient.
Example:
function log( text, opts={} ) {
const {times=1, silent=false, logger=console} = opts
if (silent) return
for (let i=0; i<times; i ) {
logger.log( text )
}
}
// all good here
log( "Hello World!", {times:1} )
// "slient" is not a valid option. It's a typo.
// I want this call to throw an exception.
log( "Bye World!", {times:2, slient:true} )
I know that I can implement it through a function that receives the names of the expected properties:
function testOpts( opts={}, optNames=[] ) { /* ... */ }
testOpts( opts, ["times", "silent", "logger"] )
But maintaining this is boring and error prone: every time I change an option I need to update those parameters. I tried it and too often I forget to update it, so my function keeps accepting parameters that I removed, and this is something I want to avoid.
Is there a better solution that lets me write the property names only once?
CodePudding user response:
You are checking whether there are properties that are not in a list of acceptable properties
First, extract the list of keys in the opts object.
Then check each key in turn against the list of acceptable keys, and give an error if it is not present.
function testOpts( opts={}, acceptableKeys=[] ) {
Object.keys(opts).forEach(key=>{
if (! acceptableKeys.includes(key)){
console.error(`Unexpected option "${key}".`)
}
})
}
testOpts( {times:2, slient:true}, ["times", "silent", "logger"] )
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
If you only want to write them once, the only way to do that is in the log
function where you are using them. There is indeed a way to do that: use rest syntax in the destructuring, and test that there are no other properties than the expected (destructured) ones:
function log( text, opts={} ) {
const {times=1, silent=false, logger=console, ...rest} = opts
// ^^^^^^^
assertEmpty(rest);
if (silent) return
for (let i=0; i<times; i ) {
logger.log( text )
}
}
function assertEmpty(val) {
const keys = Object.keys(val);
if (keys.length) {
throw new RangeError(`Unexpected properties ${keys.join(', ')} in object`);
}
}
Other than that, use a static analysis tool such as TypeScript or Flow, they will easily catch these mistakes.