UNPKG

guards

Version:

Data type & structure checking, runtime analog of types

69 lines (53 loc) 2.12 kB
/* vim:set ts=2 sw=2 sts=2 expandtab */ /*jshint asi: ture newcap: true undef: true es5: true node: true devel: true forin: true */ /*global define: true */ // Sometimes you want to validate an incoming value to determine whether to let // it pass, coerce it, or reject it. Here is an example: function Point(x, y) { return { x: x, y: y } } // It's clear that `Point` function expects `x` and `y` to be a numbers, but // what if it's called with other values ? var p1 = Point(1, 'Boom!') // `Point` will just return invalid value, causing further misbehavior of the // program. To avoid further propagation of an error, we can employ runtime // value validation: function Point(x, y) { if (typeof x !== 'number') throw new TypeError('Expected number not a: `' + x + '`') if (typeof y !== 'number') throw new TypeError('Expected number not a: `' + y + '`') return { x: x, y: y } } // Unfortunately, we ended up with a lot of code that is not relevant to a // `Point`. All we actually need is a guard of `x` and `y` properties that // either accepts values or throws. Let's refine `Point` by refactoring value // guarding logic into separate function: function number(value) { // If not a number reject! if (typeof value !== 'number') throw new TypeError('Expected number not a: `' + value + '`') // Otherwise pass! return value } function Point(x, y) { return { x: number(x), y: number(y) } } // Ok this looks much better but would be nice if our error message was less // generic so we could give a more descriptive error message, and preferably // we should not make `number` function too specific so we can reuse it in other // places. function Numbers(message) { var message = message || 'Expected number not a: `{{value}}`' return function number(value) { if (typeof value !== 'number') throw new TypeError(message.replace('{{value}}', value)) return value } } var number = Numbers('Point only takes a number arguments not a: `{{value}}') function Point(x, y) { return { x: number(x), y: number(y) } } // Maybe we can also improve our