Home > Enterprise >  Typescript. Function typing
Typescript. Function typing

Time:11-26

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.

CodePudding user response:

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;

CodePudding user response:

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

  • Related