Skip to content

Latest commit

 

History

History
184 lines (150 loc) · 3.99 KB

readme.md

File metadata and controls

184 lines (150 loc) · 3.99 KB

pass-muster

Objects work well as a single argument to a function. That way you don't have to remember the order of parameters, and your code gets a nice self-documenting look. passMuster lets you be a little more sure of those objects that you pass as an argument to functions by validating that the properties you need are there and of the right types. Think of it as a lightweight implementation of interfaces.

Of course, this isn't limited to validating objects passed as arguments. Use it wherever you want to make sure an object has what you need.

The module is written in good ol' ES5, so it works everywhere.

Usage

myFunction(opts) {
    passMuster(opts, {
        id: 'number',
        username: 'string'
    });
    // do something with opts object
}

myFunction({
    id: 007,
    username: 'jbond'
})
// --> no error thrown, myFunction continues

If myFunction is called without a required property, passMuster will throw an error:

myFunction({
    id: 007
})
// --> "missing property 'username'"

It will also throw an error if the property is not the right type:

myFunction({
    id: 007
    username: 12345
})
// --> "property 'username' has wrong type"

An array of possible types can be supplied:

let objToValidate = {
    prop: 'test'
}
passMuster(objToValidate, {
    prop: ['string', 'number']
});
// --> no error

passMuster(objToValidate, {
    prop: ['array', 'object']
});
// --> "property 'prop' not one of allowed types"

If you have further validation you want to do, a predicate function that returns true or false can be passed in:

let objToValidate = {
    num: 11
}
passMuster(objToValidate, {
    num: p => (p < 100)
});
// --> no error

passMuster(objToValidate, {
    num: p => (p % 2 === 0)
});
// --> "property 'num' does not pass predicate function"

let alsoValidate = {
    buf: new Buffer(2)
}
passMuster(alsoValidate, {
    buf: _.isBuffer // <-- using lodash
});
// --> no error

Predicate functions are executed in the context of the type object, so you can reference the expected type of other properties:

let objToValidate = {
    otherProp: 'foobar',
    num: 11
}
passMuster(objToValidate, {
    otherProp: 'string',
    num: p => (typeof p !== this.otherProp)
});
// --> no error

If you need to reference the other values passed in, the object being validated is passed as a second argument to predicate functions:

let objToValidate = {
    otherProp: 'foobar',
    num: 11
}
passMuster(objToValidate, {
    otherProp: 'string',
    num: (p, objIn) => is.not.sameType(p, objIn.otherProp) // <-- using @pwn/is, linked below
});
// --> no error

Nested objects also work:

 myFunction(opts) {
    passMuster(opts, {
        id: 'number',
        address: {
            street: 'string',
            city: 'string',
            state: 'string',
            zip: 'number'
        }
    });
    // do something with opts object
}

myFunction({
    id: 007,
    address: {
        street: '123 anywhere st',
        city: 'Los Angeles',
        state: 'CA'
    }
})
// --> "missing property 'zip'"

To mark a property as optional, put it in the special _optional block:

myFunction(opts) {
    passMuster(opts, {
        id: 'number',
        _optional: {
            username: 'string'
        }
    })
    // do something with opts object
}

myFunction({
    id: 007
})
// --> no error

myFunction({
    id: 007,
    username: 12345
})
// --> "property 'username' has wrong type"

To see the function from which you called passMuster, just look at the stack trace on the error that gets thrown.

See also

License

MIT