-
Notifications
You must be signed in to change notification settings - Fork 127
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add some
and every
?
#624
Comments
i think so. for example, i believe the only reason we have const data: Array<string | number> = ["a", "b"];
if (data.every(R.isString)) {
data; // Array<string>
} |
Right! I guess I can kind of see them being useful in control flow, for example your latter case, instead of having to write const data: Array<string | number> = ["a", "b"];
const result = R.pipe(
data
R.conditional(
[R.every(R.isString), doStringStuff],
R.conditional.defaultCase(doLooseStuff)
)
); |
hm... i'd defer to the judgment of @eranhirsch here, because i don't have a clear vision myself of what the library should be. it's certainly changed over time; compare #54 to the set of functions we have now. i do think |
I support adding these functions as it would allow us to offer a dataLast implementation, which could become lazy in the future(!) (after we fix some stuff with our lazy implementation). We can also provide some (?) type narrowing to the input: Widening example: const data = [] as number[];
if (some(data, isString)) {
console.log(data);
// ^? (number | string)[]
} or narrowing const data = [] as (number | string)[]
if (!some(data, isString)) {
console.log(data);
// ^? number[];
} (but we probably can't have both). Back to you @cjquines, wdyt? |
@eranhirsch hmm would this not be achievable (both) if we also had logic functions? Think |
The question is do we limit the narrowed type to Checkout recent PRs trying to make omitBy, pickBy, and isIncludedIn narrow correctly without breaking typing. In general typescript isn't good at conditional narrowing. |
i'm supportive of this—i think we should fix our lazy implementations and then make this lazy as well, so i'll add the v2 tag to this. thanks for all the suggestions @Brodzko ! |
Can I ask for some pointers towards what the problems are with lazy implementation? 🙏 |
There's no reason not to add it to v1 and improve it after v2 is released. As long as we get the types right, the changes to the laziness won't be breaking API changes so could be released as a minor or even a patch version. |
see the discussion in #74 in any case, i think it's possible to implement this with the current lazy infrastructure |
We can't because you'd need to push |
So I've had a go at For simplicity though, let's just assume an "identity" transform on a type guard: const wrapGuard = <T, S extends T>(predicate: (input: T) => input is S) => (input: T): input is S => predicate(input); What's interesting to me, that when using this for example in const data2 = R.pipe(
test,
R.conditional(
[wrapGuard(R.isNumber), x => {
expectTypeOf(x).toEqualTypeOf<number>() // `x` actually not narrowed
}]
)
) while simply saving the value of the resulting type guard in a separate variable does: const wrappedIsNumber = wrapGuard(R.isNumber);
const data3 = R.pipe(
test,
R.conditional(
[wrappedIsNumber, x => {
expectTypeOf(x).toEqualTypeOf<number>() // `x` correctly narrowed
}]
)
) Here is a TS playground demonstrating this. Note that I have intentionally used a trivial version of I am suspecting that in the former case, type inference "fails" inside Am I missing something important about how data-last is/should be implemented or is this coming from somewhere deeper in Typescript itself? 🤔 Cc. @eranhirsch @cjquines |
this is likely a deeper issue with typescript generic inference; i would say don't worry about the |
This is related to what I wrote about in the long comment in the other PR (#635 (comment)). Typescript would infer "forward" when it encounters a generic used as an operand, meaning that when inside You can see this by hovering over the
You can see that typescript tried to type wrapGuard's T with the typing information it had available: When you pulled out wrapGuard it didn't have any "forward" information so it used the operand itself to type "backwards". Once that type is set, you are removing a level of "generalization" from the type, so typescript has to chose narrower typing in I don't know typescript internals that much so I might be wrong about this, but this is the gist of it, you can see similar conclusions when we tried to type |
Thank you for the clarification, I suspected as much. Do you think this is something potentially addressable by the Typescript team or is this the way it has to work? I would intuitively kind of expect that in these cases, the types get inferred "bottom-up" 🤔 |
Doing a bit of refactoring, going through the usages of
lodash
in our codebase and replacing them withremeda
alternatives, I noticed there is nosome
andevery
alternatives for arrays. Is this intentional? I guess you could accomplish the same withbut that seems pretty clunky. Is the omitting of
some
andevery
intentional? If you're interested I'm happy to open a PR.Edit: mistake in implementation
The text was updated successfully, but these errors were encountered: