Open lstkz opened 6 years ago
Unfortunately, TS currently cannot handle higher-order function properly, we have to provide more information to help the inference:
const result = R.pipe<number[], number[], number[], number>(
R.filter(isEven),
R.map(n => n * n),
items => items[0],
)
const result = R.pipe(
// there're 3 possible types (List, Object, Filterable)
// defaults to Mixed (List | Object | Filterable) <-- that's why option1 works
R.filter(isEven)<'1', 'list'>(),
// contextual type inference is not working here
// since we use 0-param func to select overload.
R.map((n: number) => n * n)<'1', 'list'>(),
items => items[0],
)
After using Ramda + TS for 2 months, there are still many confusing use cases. Selectable overloads affect readability and they are not obvious to write.
Consider option 2 from above example.
It can be written as
R.pipe(
R.filter(isEven)<'1', 'list'>(),
// much simpler, no need for argument annotation, but no curring
items => items.map(n => n * n),
);
Or we can add helper methods in Ramda. I think I will be starting writing like this:
// extension for ramda
export const mapList = <T1, TR>(fn: (a: T1) => TR) => (items: T1[]): TR[] =>
R.map(fn, items);
export const filterList = <T1>(fn: (a: T1) => boolean) => (items: T1[]): T1[] =>
R.filter(fn, items);
// and then new version
R.pipe(
R.filterList(isEven),
R.mapList(n => n * n),
);
I am wondering, how people use Ramda +TS in their real projects? Or there are other alternatives for TS?
@lsentkiewicz: I agree it's a major pain point. I'm barely doing front-end anymore so can't really attest to usage well. I've hoped to resolve some of these issues by improving type inference in TS, but that definitely isn't where the short-term gains lie, especially given this isn't quite first priority to the TS team.
There does seem to be a bit of a split between TS definitions for JS FP libs (e.g. here) vs. TS-first libraries, such as those by gcanti. One might conclude that for typing purposes it'd help to use a library designed with types in mind, with structure-specific function variants such as the mapList
you mentioned.
That probably went against the grain of the ADT/Haskell philosophy Ramda intended to port to JS though, so it's a fair question what options would be out there to achieve this. Suggestions welcome.
I think Sanctuary for one tried to avoid Ramda's generic bits, though most TS FP seems to focus on ADTs. Lodash-FP is probably an option as well, though I don't know to what extent it has been typed -- last I checked Lodash typings did not seem an improvement over this repo.
If you do conclude any of the alternatives ends up superior, feel free to report back your experience -- I'd be interested whether it's actionable for improvement here or an alternative to Ramda superior for use with TS.
Maybe we'd first wanna check which parts that Ramda does we would or would not want here. What I can think of:
good:
path
and assoc
that fill such gaps in the standard library)bad:
Now, I'd made an attempt to emulate the currying part at pointfree, though typing it in any meaningful way isn't really doable yet in any generic kind of way, bar manually retyping the standard library / doing some AST transforms on TS's lib.d.ts
.
I decided to create my own utility library https://github.com/remeda/remeda. It's not pure FP like Ramda, but no manual annotations are required, and it's not painful. Works pretty well during development :)
Is it expected that we must provide all generic types?
this works
but this, reports an error
I tried to check tests https://github.com/types/npm-ramda/blob/master/tests/pipe.ts but all examples are without input data.