types icon indicating copy to clipboard operation
types copied to clipboard

R.converge bad signature in @types/ramda

Open adellamaggiora opened this issue 3 years ago • 9 comments

the following function declaration generates a typescript compile error:

const userMatchRoles = user => roles => R.converge(R.or, [R.includes(user.role), R.isEmpty])(roles);

of course the error is not raised due to typescript strict mode (set to false) but from a bad signature in @types/ramda

adellamaggiora avatar May 27 '22 12:05 adellamaggiora

@adellamaggiora: We have people now looking into bringing the TS typings in-house. It's not clear how long that will take, but in the meantime, you might want to bring it up with the DefinitelyTyped folks.

Just out of curiosity, is there any real reason for the inner lambda? It looks like this would do the same job and be simpler:

const userMatchRoles = user => R.converge(R.or, [R.includes(user.role), R.isEmpty])

I would also replace converge with lift whenever I can (which isn't always!) So this looks better still to me:

const userMatchRoles = user => R.lift(R.or)(R.includes(user.role), R.isEmpty)

CrossEye avatar May 27 '22 18:05 CrossEye

@adellamaggiora Because all functions of ramda are auto curried, they do not work well together with ts.

By now, one solution for ramda user is that: if a ramda api used as an argument of other functions, people should always explicitly write all the parameters and types to help ts compiler infer the right type, as follows:

const userMatchRoles = (user: { role: string }) => (roles: string[]) =>
    R.converge((a: boolean, b: boolean) => R.or(a, b), [R.includes(user.role), R.isEmpty])(roles);

@valerii15298 Can you look at this issue in spare time?

adispring avatar May 28 '22 00:05 adispring

The signature for converge in @types/ramda is intended to work like that. Typescript cannot infer which arity of R.or to use and is using arity of 1 automatically. The description of how to solve it, is explained in tests here

@adispring solution seems most accurate to me.

Sometimes you need more control and second option to use is this:

    const userMatchRoles = (user: any) => (roles: any) =>
        R.converge((...args) => R.or(...args), [R.includes(user.role), R.isEmpty] as const)(roles);

Of course better specify correct types for user and roles arguments instead of any.

valerii15298 avatar May 28 '22 08:05 valerii15298

By now, one solution for ramda user is that: if a ramda api used as an argument of other functions, people should always explicitly write all the parameters and types to help ts compiler infer the right type, as follows:

In order to help people using ramda apis with typescript, I think we should add some guide or tips on how to using ramda apis with typescript more effectively. In the guide, we can list what are the best ways and what should be avoid to do when write ramda with ts.

adispring avatar May 30 '22 00:05 adispring

Thanks everybody for the support, I'll follow the advice to make my ts code to work.

It would be nice having a documentation for Ramda ts api too.

adellamaggiora avatar May 30 '22 08:05 adellamaggiora

@adellamaggiora: We have people now looking into bringing the TS typings in-house. It's not clear how long that will take, but in the meantime, you might want to bring it up with the DefinitelyTyped folks.

Just out of curiosity, is there any real reason for the inner lambda? It looks like this would do the same job and be simpler:

const userMatchRoles = user => R.converge(R.or, [R.includes(user.role), R.isEmpty])

I would also replace converge with lift whenever I can (which isn't always!) So this looks better still to me:

const userMatchRoles = user => R.lift(R.or)(R.includes(user.role), R.isEmpty)

i'm pretty new using functional paradigm and this is my first real big project using it. I didn't know operator lift, thanks!

I did without inner lamda too but at the end i put it in my code cause the signature of the function seems more clear (saying what exact parameters it accepts).

adellamaggiora avatar May 30 '22 08:05 adellamaggiora

another ts compile error with the following code:

` const pluckObjectsProps = (data: Object[]) => R.pipe( R.pick, R.map(R.__, data) )

const data = [ { id: 1, name: 'Pippo', age: 100 }, { id: 2, name: 'Pluto', age: 150 } ];

const plucked = pluckObjectsProps(data)(['name', 'age']); `

I am having serious difficulties using ramda with typescript and I think it will not be possible for now to use this library with types. I'll try plain javascript without the benefits of vscode linter suggestion 😔. Thanks anyway for the support.

adellamaggiora avatar May 30 '22 09:05 adellamaggiora

This is a good example to show what's the most frequently problems people encounter again and again, when writing ramda with typescript:

When using each ramda api separately with ts, it may works normal most of the time;

But when composing multiple ramda apis with R.compose, R.converge and other high order ramda functions, things are getting worse. Point-free style code make things more worse. Problems occurs here and there, people even can not write a simple function by composing ramda apis. then they are disappoint, and give up finally. They may then use native esnext apis instead.

Ramda is a practical Functional JavaScript library, but not a practical functional typescript library by now.

adispring avatar May 31 '22 08:05 adispring

@adispring: How much of that problem do you imagine we'll be able to fix by bringing the typings in-house? Will it resolve a great deal of it, or are the issues with currying and HKTs too insurmountable for us to be able to significantly reduce this tension?

CrossEye avatar May 31 '22 14:05 CrossEye