4

I'm trying to get better with FP but struggling how to deal with typing generic "curry" functions.

For example, I've written a "Curry" version of reduce:

const reduce = <S, R>(fn: (result: R, obj: S, i?: number, arr?: S[]) => R, init: R) => 
(objects: S[]) => 
objects.reduce(fn, init);

const numToSum: number[] = [1,2,3,5,8,13];

const result = reduce((sum, n) => sum + n, 0)(numToSum);

The problem is that typescript obviously can't know the type of S until you actually call the curry function with numToSum.

It's more obvious when you see it without the curry call:

const sumNumbersFn = reduce((sum, n) => sum + n, 0);

In this case you can fix this by typing the arguments of the function itself with n: number or explicitly setting the generic parameters <number[], number>.

The former seems reasonable, but what I'm running into is that as things get more complex, I'm constantly having to provide generic signature.

I'm wondering if I am missing something, and it is actually possible to get typescript to "infer" the types from the later function calls?

Going back to the first example:

const result = reduce((sum, n) => sum + n, 0)(numToSum);

It seems like the compiler actually SHOULD have all the information needed to infer types.

Maybe my types are just off?

Update

Here's a more concrete/full example of the problem I'm running into

TS-Playground

4
  • I know that stackoverflow.com/questions/60203078/… is very similar (in title)... I think at it's core the question is different, but if you think this is a duplicate, my feelings won't be hurt Commented Nov 14, 2020 at 17:43
  • I'm also not sure that what I'm doing is even correct fp. I wouldn't be surprised if what I've written above is a incorrect application Commented Nov 14, 2020 at 17:44
  • I don't think TS can ever infer the generic types from the value you eventually pass to the function returned - you could assign the return value of reduce and call it with multiple different values, for example. Commented Nov 14, 2020 at 17:49
  • @jonrsharpe if you have a moment, check out the ts-playground link I just added... It probably does a better job of describing the problem I'm trying to overcome Commented Nov 14, 2020 at 17:57

1 Answer 1

3

All you have to do is to declare a function which you return from select function as generic:

function select<T, S>(sFn: (obj: T) => S) {
  function computation<G>(fn: (obj: S) => G) {
    return (obj: T) => fn(sFn(obj));
  }

  return computation;
} 

P.S: I don't know why such kind of syntax doesn't work in TS playground:

const select = <T, S>(sFn: (obj: T) => S) => <G>(fn: (obj: S) => G) => {
  return (obj: T) => fn(sFn(obj));
}

Maybe because of some TS config settings. Thus I wrote the solution as a function declaration instead of a function expression.

Sign up to request clarification or add additional context in comments.

1 Comment

ah man... I trashed so hard and was sooo close. I actually tried the "function expression" version along the way (probably multiple times). Your a hero!

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.