3

Hello I am learning typescript and i follow this exercises: Link

I have problem understanding how should i proced with this example.

there is a code like this:

export function map(mapper, input) {
    if (arguments.length === 0) {
        return map;
    }
    if (arguments.length === 1) {
        return function subFunction(subInput) {
            if (arguments.length === 0) {
                return subFunction;
            }
            return subInput.map(mapper);
        };
    }
    return input.map(mapper);
}

and I am supposed to add types to this. I managed to create something like this:

export function map<T>(mapper: Function, input : T[]| any) : T[] | Function {
    if (arguments.length === 0) {
        return map;
    }
    if (arguments.length === 1) {
        return function subFunction(subInput: T[]|any): T[] | Function {
            if (arguments.length === 0) {
                return subFunction;
            }
            return subInput.map(mapper);
        };
    }
    return input.map(mapper);
}

typescript do not returns compilation errors now but i still fail the test. I do not understand what it is expected from me to make this work.

I can check suggested answer but whe i look at it this is dark magic for me.

I could check test.ts for what is expected but notation like const mapResult1 = map()(String)()([1, 2, 3]); is looking very strange for me.

2
  • 2
    That's....a very strange function, and certainly not a beginner assignment. It makes me wonder about the quality of the course/exercises. Commented Nov 25, 2021 at 18:15
  • The first thing to start Array.map function receives a parameter and transforms it into another. So you need at least 2 generic parameters 1 is for arrays element type and 1 is for the output type. Commented Nov 25, 2021 at 18:19

2 Answers 2

1

Well it's just some use case of the map function:

  • When writing map() the result is map itself
  • The next step is map(String) which returns subFunction
  • The next step is subFunction() which returns subFunction itself
  • The last step is subFunction([1, 2, 3]) which returns the result of the expression [1, 2, 3].map(String): ['1', '2', '3']

The type of this result is a string[] because every element of the final array is the result of String being called as a function is always a string. Your typings are supposed to resolve this type without even executing the code.

Here is one possible solution found in the source code of the exercises you are doing. They actually revamped the implementation to make it "functional" (return a new function for every unresolved parameter)

function toFunctional<T extends Function>(func: T): Function {
    const fullArgCount = func.length;
    function createSubFunction(curriedArgs: unknown[]) {
        return function(this: unknown) {
            const newCurriedArguments = curriedArgs.concat(Array.from(arguments));
            if (newCurriedArguments.length > fullArgCount) {
                throw new Error('Too many arguments');
            }
            if (newCurriedArguments.length === fullArgCount) {
                return func.apply(this, newCurriedArguments);
            }
            return createSubFunction(newCurriedArguments);
        };
    }
    return createSubFunction([]);
}

interface MapperFunc<I, O> {
    (): MapperFunc<I, O>;
    (input: I[]): O[];
}

interface MapFunc {
    (): MapFunc;
    <I, O>(mapper: (item: I) => O): MapperFunc<I, O>;
    <I, O>(mapper: (item: I) => O, input: I[]): O[];
}

/**
 * 2 arguments passed: returns a new array
 * which is a result of input being mapped using
 * the specified mapper.
 *
 * 1 argument passed: returns a function which accepts
 * an input and returns a new array which is a result
 * of input being mapped using original mapper.
 *
 * 0 arguments passed: returns itself.
 */
export const map = toFunctional(<I, O>(fn: (arg: I) => O, input: I[]) => input.map(fn)) as MapFunc;
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the explanation so the problem is my how this typing should look like?
I tried to solve your problem but I couldn't without changing the implementation. Indeed, the if (arguments.length === 0) lines do not trigger any type guard in TypeScript so according to me you'd have to change these conditions to if (mapper === undefined) and if (subInput === undefined)
I actually found the solution here github.com/typescript-exercises/typescript-exercises/blob/… it confirms that the implementation has to be changed. I guess you should adapt the difficulty of the exercises you choose because those are kinda hard!
1

Here is a simple solution:

interface SubFunction<I, O> {
  (): SubFunction<I, O>;
  (input: I[]): O[];
}

function map<I, O>(): typeof map;
function map<I, O>(mapper: (i: I) => O): SubFunction<I, O>;
function map<I, O>(mapper: (i: I) => O, input: I[]): O[];

function map<I, O>(mapper?: (i: I) => O, input?: I[]) {
  if (mapper && input) {
    return input.map(mapper);
  }
  if (mapper) {
    const subFunction = (input?: I[]) => input ? input.map(mapper) : subFunction;
    return subFunction;
  }
  return map;
}

const mapResult1 = map()(String)()([1, 2, 3]);
  • It uses a helper type for the SubFunction.
  • It uses idiomatic modern TypeScript (no function apart from top-level, no arguments)
  • It uses function overload to have different return types.

Playground

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.