diff --git a/packages/@ember/runloop/index.js b/packages/@ember/runloop/index.js index fdca2d426ca..52a2f35b035 100644 --- a/packages/@ember/runloop/index.js +++ b/packages/@ember/runloop/index.js @@ -217,7 +217,39 @@ export function join() { @since 1.4.0 @public */ -export const bind = (...curried) => (...args) => join(...curried.concat(args)); +export const bind = (...curried) => { + if (!bindMethodPresent(...curried)) { + throw new Error('could not find a suitable method to bind'); + } + return (...args) => join(...curried.concat(args)); +}; + +/** + Applies the same logic as backburner parseArgs for detecting if a method + is actually being passed. + + @method bindMethodPresent + @private + @param {Object|Function} methodOrTarget Same as first argument for backburner.join + @param {Function|string} methodOrArg Same as second argument for backburner.join + @return {Boolean} whether there is a method present or not + */ +function bindMethodPresent(methodOrTarget, methodOrArg) { + let length = arguments.length; + + if (length === 0) { + return false; + } else if (length === 1) { + return typeof methodOrTarget === 'function'; + } else { + let type = typeof methodOrArg; + return ( + type === 'function' || // second argument is a function + (methodOrTarget !== null && type === 'string' && methodOrArg in methodOrTarget) || // second argument is the name of a method in first argument + typeof methodOrTarget === 'function' //first argument is a function + ); + } +} /** Begins a new RunLoop. Any deferred actions invoked after the begin will diff --git a/packages/@ember/runloop/tests/run_bind_test.js b/packages/@ember/runloop/tests/run_bind_test.js index bd04427a6c4..82c112a0203 100644 --- a/packages/@ember/runloop/tests/run_bind_test.js +++ b/packages/@ember/runloop/tests/run_bind_test.js @@ -36,5 +36,24 @@ moduleFor( asyncFunction(bind(asyncCallback, asyncCallback, 1)); } + + ['@test bind throws an error if callback is undefined'](assert) { + let assertBindThrows = (msg, ...args) => { + assert.throws( + function() { + bind(...args); + }, + /could not find a suitable method to bind/, + msg + ); + }; + assertBindThrows('without arguments'); + assertBindThrows('with one arguments that is not a function', 'myMethod'); + assertBindThrows( + 'if second parameter is not a function and not a property in first parameter', + Object.create(null), + 'myMethod' + ); + } } );