检测意外属性

Detecting unexpected properties

我经常使用对象来传递一组选项。

在 99.9% 的情况下,我希望这些对象只包含我使用的 属性 的一个子集。如果出现意想不到的 属性,这几乎总是意味着存在拼写错误或逻辑错误。

有没有一种简单的方法可以确保选项对象不包含意外属性?我只在测试和调试期间真正需要它,所以它不一定是高效的。

示例:

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} )

我知道我可以通过接收预期属性名称的函数来实现它:

function testOpts( opts={}, optNames=[] ) { /* ... */ }
testOpts( opts, ["times", "silent", "logger"] )

但是维护这个很无聊而且容易出错:每次我改变一个选项我都需要更新那些参数。我试过了,但我经常忘记更新它,所以我的函数一直接受我删除的参数,这是我想避免的事情。

有没有更好的解决方案让我只写一次 属性 个名字?

如果您只想编写一次,唯一的方法是在您使用它们的 log 函数中。确实有一种方法可以做到这一点:使用 rest syntax in the destructuring,并测试除了预期的(解构的)属性之外没有其他属性:

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`);
    }
}

除此之外,使用TypeScript或Flow等静态分析工具,他们很容易发现这些错误。