-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
isAnyOf/isAllOf HOFs for simple matchers #788
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
isAnyOf/isAllOf HOFs for simple matchers #788
Conversation
Deploy preview for redux-starter-kit-docs ready! Built with commit c838179 https://deploy-preview-788--redux-starter-kit-docs.netlify.app |
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit c838179:
|
Looks nice! Two minor nitpicky questions:
|
I can definitely put these both in About the de-duplication, what I could de-duplicate is the inner... if (hasMatchFunction(matcher)) {
return matcher.match(action)
} else {
return matcher(action)
} ... as a const matches = (matcher: Matcher<any>, action: any) => {
if (hasMatchFunction(matcher)) {
return matcher.match(action)
} else {
return matcher(action)
}
}
export function isAnyOf<Matchers extends [Matcher<any>, ...Matcher<any>[]]>(
...matchers: Matchers
) {
return (action: any): action is ActionMatchingAnyOf<Matchers> => {
return matchers.some((matcher) => matches(matcher, action));
}
}
export function isAllOf<Matchers extends [Matcher<any>, ...Matcher<any>[]]>(
...matchers: Matchers
) {
return (action: any): action is ActionMatchingAllOf<Matchers> => {
return matchers.every((matcher) => matches(matcher, action));
}
} If I combine them into |
PR is updated. After de-duplicating the code, moving them into the same file made a lot of sense. |
Yeahhhhh, I like the way that looks :) We haven't had a specific "one function per file" convention - it's just sort of ended up that way because we have some fairly sizeable implementations and types for each of our core APIs. Obviously this isn't a huge change, but it does save a few bytes in the final output. |
Just out of curiosity, could you use an optional chaining check there instead of a try/catch? |
That should be fine. I'm guessing you're concerned about potential performance issues? I tried that, but the build fails with I could do the manual equivalent if you prefer. I was erring on the side of being careful about not breaking things. |
In theory, the original code was perfectly safe. The only arguments that could be passed in (due to type-checking) were either a function, or an object with a Or, let me know what you think about... export const hasMatchFunction = <T>(
v: Matcher<T>
): v is HasMatchFunction<T> => {
return v && typeof (v as HasMatchFunction<T>).match === 'function'
} I'm not sure the extent to which the code should be forgiving of mistakes that wouldn't be caught by people using non-TypeScript RTK. If we're being very careful about that, perhaps I should also confirm the For example, the following (combined with the above) would prevent const matches = (matcher: Matcher<any>, action: any) => {
if (hasMatchFunction(matcher)) {
return matcher.match(action)
} else if (typeof matcher === 'function') {
return matcher(action)
} else {
return false;
}
} Or do you prefer to let an exception be thrown so they'll recognize their mistake? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So far, this looks very good. Could you add some type tests as well? (See the separate type-tests
folder.
src/tsHelpers.ts
Outdated
try { | ||
return typeof (v as HasMatchFunction<T>).match === 'function' | ||
} catch { | ||
return false | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, optional chaining is still not an option, since we have tests running back to pre-optional-chaining TS versions.
But I'd say something like
try { | |
return typeof (v as HasMatchFunction<T>).match === 'function' | |
} catch { | |
return false | |
} | |
return v && typeof (v as HasMatchFunction<T>).match === 'function' |
would totally suffice here.
No need to provoke & catch an exception if it can be prevented in the first place.
I'm usually be all in for an error to be thrown early in development opposed to something like this staying hidden because of some graceful recovery - so I say just let it throw. |
Of course. Are there any specific cases I should make sure to cover? I've also replaced the |
Nothing specific comes to mind - check the type tests for One thing: maybe add extra tests that the combined matchers don't widen the type to const actionNotAny: IsAny<typeof action, true, false> = false should so the trick. Edit: I had this the wrong way round first. Haven't had coffee yet -.- |
It's been a busy day. I'll be able to add the type tests tomorrow or the day after at the latest. |
I've added some type tests for I'm surprised I haven't heard of |
This looks very good to me. Let's continue with the thunk-related stuff, alternative signatures and docs in three seperate PRs. You still up for it? :) |
Honestly, there are a lot better tools around, like |
Absolutely. I'll start with the extra thunk-related stuff first. Do you have any thoughts about where these functions should be mentioned in the docs, when I start on that? |
I guess we should add an extra point |
nice! |
Implementation for features requested in #771, based on discussion there.
Provides higher order functions for matching actions against many type guards or action creators.
Example usage:
Currently implements only core functionality.