Skip to the code
cf.

Compose

Write compose(...fns). It returns one function that runs fns from right to left: compose(f, g, h)(x) is f(g(h(x))).

The rightmost function gets all the arguments and the caller's this; every other function gets the previous result as its only argument.

With no functions, compose() returns a function that returns its first argument. If any argument isn't a function, compose throws a TypeError right away, before anything is called. The composed function can be called any number of times.

const shout = compose((s) => s + "!", (s) => s.toUpperCase());
shout("hi"); // "HI!"
 
const doubleSum = compose((n) => n * 2, (a, b) => a + b);
doubleSum(2, 3); // 10

What the tests check

  1. runs the functions from right to left
    expect(compose((x) => x + 1, (x) => x * 2)(3)).toBe(7);
    expect(compose((s) => s + "a", (s) => s + "b", (s) => s + "c")("")).toBe("cba");
    
  2. the rightmost function gets all the arguments
    expect(compose((x) => x * 10, (a, b) => a - b)(5, 2)).toBe(30);
    
  3. the other functions get only the previous result
    const outer = spy((x) => x);
    compose(outer, (a, b) => a + b)(1, 2);
    expect(outer.calls[0].args).toEqual([3]);
    
  4. passes the caller's this to the rightmost function
    const obj = {
      n: 4,
      next: compose(
        (x) => x + 1,
        function () {
          return this.n;
        },
      ),
    };
    expect(obj.next()).toBe(5);
    
  5. with no functions, returns the first argument
    const value = { id: 1 };
    expect(compose()(value)).toBe(value);
    expect(compose()(5, 6)).toBe(5);
    
  6. with one function, works like that function
    expect(compose((a, b) => a * b)(3, 4)).toBe(12);
    
  7. gives the same answer every time it's called
    const addBA = compose((s) => s + "a", (s) => s + "b");
    expect(addBA("")).toBe("ba");
    expect(addBA("")).toBe("ba");
    expect(addBA("x")).toBe("xba");
    
  8. throws a TypeError right away if an argument isn't a function
    expect(() => compose((x) => x, 42)).toThrow(TypeError);
    expect(() => compose("upper")).toThrow(TypeError);
    

Learn more

solution.jsSaved in this browser

Run tests runs every test on your code, here in your browser. The problem is solved when they all pass.

Ctrl + Enter runs the tests (⌘ + Enter on a Mac).