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.