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
feat(ofType): narrow type of output based on action type #749
Conversation
Thanks for the PR! Can you please add a detailed description on why this change is helpful and any downsides? The linked comment is helpful somewhat but not complete and also it's better to have everything here. Thanks again. |
The reason behind the change is that a generic type param can only be inferred to literal type when it extends from a primitive type. function foo<T extends string>(x: T): T {
return x;
}
function bar<T>(x: T): T {
return x;
}
let x = foo('a'); // type of x is literal "a"
let y = bar('b'); // type of y is string |
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.
I see what you want to achieve and I think it's valuable for developer experience but I'm afraid the downside outweighs the upside.
I tried some Conditional Type
Type extends (Input['type'] extends string ? string : any),
but it doesn't work.
If you managed to make it work, we could go from here.
src/operators.ts
Outdated
@@ -17,7 +17,7 @@ export function ofType< | |||
// All possible actions your app can dispatch | |||
Input extends Action, | |||
// The types you want to filter for | |||
Type extends Input['type'], | |||
Type extends Input['type'] & string, |
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.
I don't think you can do that because
export interface Action<T = any> {
type: T
}
meaning it can be any
thing, not just a string
(even though I agree the common usage is string
)
If we do that, we won't be compatible with people not using string
as their "type"
.
Hello, @MaximeBernard, thanks for your review. |
LGTM but I'm afraid I'm missing something. Any thoughts @evertbouw / @benlesh? |
What happens if the type wasn't found? In other approaches it would silently narrow to |
The template param |
If you use a number that wasn't part of the union then it does error in the way I was afraid of. The ofType call looks fine but then in the map function you suddenly have a For strings it seems to work just fine. The vast majority of Redux users will use strings so I can accept this. |
Yeah, Typescript seems to also infer something to literal type in a const context. But in a real-world scenario, one has to add of<MyAction>().pipe(
ofType('a' as const),
map((x) => x.foo), // okay, x is `{type: 'a', foo: string}`
);
of<MyAction>().pipe(
ofType('a'), // not inferred to literals without `as const`
map((x) => x.foo), // does not compiled, x is still `MyAction`
); |
Closing old PRs. As always thank you for your contribution. |
Any chance to get it reviewed and merged? @evertbouw |
See #622 (comment) for detail