Typescript complaining when I rewrite a generic 3 arity function as a curried version

Viewed 17

Hi and first off thanks to anyone taking the time to read this.

Background

I have a function called "normalFunc" in typescript like such:

import * as TE from 'fp-ts/TaskEither';
import { User } from './some-api-library';

// Next function is just a dummy version of an actual api function we have. I added to make the type defs clear.
const searchById = (id: string): Promise<User | null> => Promise.resolve(null)

// The actual function I'm interested in
const normalFunc = <I, O, E>(eFn: (e: unknown) => E, lp: (id1: I) => Promise<O | null>, id2: I) => TE.tryCatch(() => lp(id2), eFn)

Notice normalFunc is a regular arity 3 function, no big deal here. It takes a function that will return some value to signify an error, then a function which takes a string and returns a promise that can return a nullable, and finally some value that will later be used as the input for the previous function that returns the promise.

I can use like so:

const testnormal = normalFunc(e => Error(`${e}`), searchById, '0001');

Typescript understands and resolves the generic types <I, O, E> just fine.

The problem:

If I try to write normalFunc as a curried function:

// Notice this is function is typed exactly the same as the normal version, at least it looks like it to me.
// I actually copied the normal version and just changed a few characters to add the arrows and some necessary parenthesis.
const curriedFunc = <I, O, E>(eFn: (e: unknown) => E) => (lp: (id1: I) => Promise<O | null>) => (id2: I) => TE.tryCatch(() => lp(id2), eFn)

Perhaps it is easier to read if I rearrange it like this:

// Same function as above but nicer format
const curriedFunc = <I, O, E>(eFn: (e: unknown) => E) =>
                               (lp: (id1: I) => Promise<O | null>) =>
                                 (id2: I) =>
                                   TE.tryCatch(() => lp(id2), eFn)

This time when I try to use it, I get a typescript error in the second arity/function call in the chain:

const testCurried = curriedFunc(e => Error(`${e}`))(searchById)('0001');

// Typescript complains (highlights) the second function call in the chain (searchById)
// It says that:
//   Argument of type '(id: string) => Promise<User | null>' is not assignable to
//   parameter of type '(id1: unknown) => Promise<unknown>'.
//     Types of parameters 'id' and 'id1' are incompatible.
//       Type 'unknown' is not assignable to type 'string'.ts(2345) 

I'm still completely baffled by this error. Perhaps generic functions using multiple arrows for currying need a different syntax? Typescript didn't seem to mind when I rewrote the normal function to make it curried with my use of the generic types <I, O, E>. But I don't understand why the types don;t seem to check anymore if the function is almost identical except for the curried rewrite.

If anyone has any idea of what is happening here I'll deeply appreciate it.

0 Answers
Related