diff --git a/README.md b/README.md index 7081182..75becc4 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Typester is inspired by the [check-types](https://www.npmjs.org/package/check-ty * Allow a single `isA()` check to be used for what would otherwise have to be done with both `instanceof` and `typeof()`checks, as appropriate. * Provide a `fulfills()` method that can verify any object using shape-based duck typing. * Allow `isA()` checks to efficiently verify multiple inheritance (e.g. sand-boxed mix-ins and implemented interfaces) for applications that themselves used [topiarist](https://github.com/BladeRunnerJS/topiarist) to set these up. + * Failing if a method is invoked with more arguments than are expected. Here's how you might use Typester to implement `addEventListener()`: @@ -37,6 +38,7 @@ Some things to note here: * The `fulfills()` method can be used do shape-based checks. * The `optionally` modifier can be used for optional arguments. * It's the developer's responsibility to ensure the `verify()` statements are performed in the correct order — the provided argument name is only used for informational purposes when an error is thrown. + * it's the developer's responsibility to ensure that verifications are provided for all arguments — otherwise an error will be thrown. ## Verifiers diff --git a/lib/ArgVerifier.js b/lib/ArgVerifier.js index 8856fe2..950b0ec 100644 --- a/lib/ArgVerifier.js +++ b/lib/ArgVerifier.js @@ -9,6 +9,10 @@ function verifierMethod(verifier, methodName) { verifier[methodName].apply(this, arguments); } + if(this.argsVerifier.argIndex < this.argsVerifier.arguments.length) { + this.argsVerifier.constructor.pendingVerifier = this.argsVerifier; + } + return this.argsVerifier; }; } diff --git a/lib/ArgsVerifier.js b/lib/ArgsVerifier.js index 46dc855..0643d8c 100644 --- a/lib/ArgsVerifier.js +++ b/lib/ArgsVerifier.js @@ -2,6 +2,7 @@ var ArgVerifier = require('./ArgVerifier'); var ArgumentError = require('./ArgumentError'); function ArgsVerifier(arguments) { + if(ArgsVerifier.pendingVerifier) throw pendingVerifierError(); if(arguments === undefined) throw new ArgumentError('arguments argument must be provided'); this.arguments = arguments; @@ -9,9 +10,19 @@ function ArgsVerifier(arguments) { } ArgsVerifier.prototype.verify = function(argName) { + ArgsVerifier.pendingVerifier = null; if(typeof(argName) != 'string') throw new TypeError('argName argument must be a String'); return new ArgVerifier(this, argName, this.arguments[this.argIndex++]); }; +function pendingVerifierError() { + var pendingVerifier = ArgsVerifier.pendingVerifier; + var pendingVerifierArgs = Array.prototype.slice.call(pendingVerifier.arguments); + ArgsVerifier.pendingVerifier = null; + + return new ArgumentError('only ' + pendingVerifier.argIndex + ' argument(s) verified but ' + pendingVerifierArgs.length + + ' were provided: [' + pendingVerifierArgs.join(', ') + ']'); +} + module.exports = ArgsVerifier; diff --git a/spec/test/typester.js b/spec/test/typester.js index 4fa9de9..bd41df9 100644 --- a/spec/test/typester.js +++ b/spec/test/typester.js @@ -86,5 +86,23 @@ describe('typester', function() { func.bind(func, '10', true).should.throw(TypeError); func.bind(func, '10', true).should.throw('num argument must be a Number'); }); + + it('throws an error if not all of the arguments from the previous verification were accounted for', function() { + function func() { + using(arguments) + .verify('bool').isA(Boolean) + .verify('num').optionally.isA(Number); + } + + function nextVerification() { + using([]); + } + + func(true, 10, 'text'); + nextVerification.should.throw(ArgumentError); + + func(true, 10, 'text'); + nextVerification.should.throw('only 2 argument(s) verified but 3 were provided: [true, 10, text]'); + }); }); });