diff --git a/src/common/hof.ts b/src/common/hof.ts index 2944308a..d25dd850 100644 --- a/src/common/hof.ts +++ b/src/common/hof.ts @@ -45,22 +45,17 @@ import { Predicate } from './common'; * * ``` * - * Stolen from: http://stackoverflow.com/questions/4394747/javascript-curry-function - * * @param fn * @returns {*|function(): (*|any)} */ export function curry(fn: Function): Function { - const initial_args = [].slice.apply(arguments, [1]); - const func_args_length = fn.length; - - function curried(args: any[]) { - if (args.length >= func_args_length) return fn.apply(null, args); - return function() { - return curried(args.concat([].slice.apply(arguments))); - }; - } - return curried(initial_args); + return function curried() { + if (arguments.length >= fn.length) { + return fn.apply(this, arguments); + } + const args = Array.prototype.slice.call(arguments); + return curried.bind(this, ...args); + }; } /** diff --git a/test/hofSpec.ts b/test/hofSpec.ts new file mode 100644 index 00000000..127816a3 --- /dev/null +++ b/test/hofSpec.ts @@ -0,0 +1,59 @@ +import { curry } from '../src/common'; + +describe('curry', () => { + let original: jasmine.Spy; + beforeEach(() => { + original = jasmine.createSpy('original', function(a, b, c) {}); + }); + + it('should accept a function and return a new function', () => { + expect(typeof curry(original)).toBe('function'); + }); + + it('should return a function that invokes original only when all arguments are supplied', () => { + const curried1 = curry(original); + curried1(1, 2, 3); + expect(original).toHaveBeenCalledTimes(1); + expect(original).toHaveBeenCalledWith(1, 2, 3); + }); + + it('should pass extra arguments through to original function', () => { + const curried1 = curry(original); + curried1(1, 2, 3, 4); + expect(original).toHaveBeenCalledTimes(1); + expect(original).toHaveBeenCalledWith(1, 2, 3, 4); + }); + + it('should keep returning functions that return functions if no arguments are supplied', () => { + const curried1 = curry(original); + expect(typeof curried1).toBe('function'); + + const curried2 = curried1(); + expect(typeof curried2).toBe('function'); + + const curried3 = curried2(); + expect(typeof curried3).toBe('function'); + + const curried4 = curried3(); + expect(typeof curried4).toBe('function'); + + const curried5 = curried4(); + expect(typeof curried5).toBe('function'); + + const curried6 = curried5(); + expect(typeof curried6).toBe('function'); + + expect(original).toHaveBeenCalledTimes(0); + }); + + it('should keep returning functions that return functions until all arguments are supplied', () => { + const curried1 = curry(original); + const curried2 = curried1(1); + const curried3 = curried2(2); + const result = curried3(3); + + expect(result).toBeUndefined(); + expect(original).toHaveBeenCalledTimes(1); + expect(original).toHaveBeenCalledWith(1, 2, 3); + }); +});