-
Notifications
You must be signed in to change notification settings - Fork 3k
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(groupBy): Support named arguments, support ObservableInputs for duration selector #5679
Conversation
@benlesh IDK what is going on with the inference. If I hover over the But if I hover over the FWIW, this is in VS Code using TS 4.0.2. |
Hey, I think this is an issue in Typescript. Note that if, as a consumer, you set the type of the parameter for the key selector: Then it picks up the type correctly. The error shown is then because key is a string, but I remembered this because I had the same problem some years ago, in a very similar interface, and raised this issue microsoft/TypeScript#25092. |
@benlesh I don't think this is related to the issues @voliva referenced - there is no free type parameter here, The fact that the inference is made if Additionally, if a temporary property Consequently, I agree with Victor that this appears to be a TS issue. The fact that the popup shows that the inference of |
There is a TS playground repro here. Note that the discrepancy between popups isn't present in the playground. |
I'm sorry to insist (and for the extra noise when it probably doesn't really matter) - But I still think that it's essentially the same issue I refereneced. Note that in that issue, there's also no free type parameter: interface MyInterface<T> {
retrieveGeneric: (parameter: string) => T,
operateWithGeneric: (generic: T) => string
}
inferTypeFn({
retrieveGeneric: parameter => 5,
operateWithGeneric: generic => generic.toFixed() // Fails .toFixed() not in generic
}); Semantically, T should be infered to Likewise, in interface GroupByOptions<T, K> {
key: (value: T) => K;
duration?: (grouped: GroupedObservable<K, T>) => ObservableInput<any>;
subject?: () => Subject<T>;
}
groupBy({
key: val => val,
duration: (group) => durations[group.key] // Fails `key` unknown
}) Semantically, K should be infered to 'string', but TS fails to do so. I reckon the only difference between these two examples is that the generic name that TS fails to properly infer is As an alternative, I'd suggest two options, but both have drawbacks :(
|
@voliva Yeah, I see what you mean regarding your issue. It's not immediately apparent why your issue was closed in favour of the issue that explicitly mentions free type parameters. The reason is given in this comment:
The same comment also explains what's happening here:
That seems to suggest it infers I guess this is not going to be fixed anytime soon. It's curious that the VS Code extension's tooling gets it right, though. |
…duration selector - Adds support for named arguments. - Adds support for returning promises, et al, from the duration selector. NOTES: * There's a problem with type inferrence from the options `duration` selector that is noted in a `TODO` in the code here. * The tests for `groupBy` appear to be EXTREMELY old and outdated, and I was unable to updated them easily to use run mode. We may have to rewrite them all at some point to use better techniques. The issue seems to be a rudementary means of testing the inner observables that is incompatible with run mode. * Docs still need updated * Older paths still need to be deprecated * dtslint tests need to be added
b21d811
to
9c26c40
Compare
Well. I've updated this PR... however we still have the same issue. So. I'm not sure what we can do about it. Super weird. |
I think both of these alternatives are still valid:
1 means that consumers will need to add the specific generics by themselves while the issue in TS is not fixed, e.g. obs.pipe(groupBy<string, string>({
key: (val) => val,
duration: (group) => durations[group.key]
})).subscribe(); 2 means changing the new options overload so that the duration selector is placed in a different parameter than the key selector, e.g. obs.pipe(groupBy(
(val) => val,
{
duration: (group) => durations[group.key]
}
)).subscribe(); (note that in the test it will still complain but for a good reason: On my personal preference, I'd go with (2). I think it also makes sense to always have the parameter be the key selector, and optionally take in an options object, while deprecating all the old overloads. |
Definite 'no' for 1 ☝️ from me. 2 is fine, though. Explicit type parameters - i.e. type parameters as type assertions - are something to be avoided wherever possible, IMO, and if they - or an explicit type annotation - are necessary to work around a shortcoming in TypeScript's behaviour, I would like to avoid it. It's just going to confuse people and they are going to open an issue, etc. Anyway, the key selector always needs to be specified, so if it's a separate argument, it's very much like the |
Okay, I've switched to |
const source = e1 | ||
.pipe(groupBy( | ||
(val: string) => val.toLowerCase().trim(), | ||
(val: string) => val, | ||
(group: any) => group.pipe(skip(2)) | ||
)); | ||
.pipe( | ||
groupBy(val => val.toLowerCase().trim(), { | ||
duration: group => group.pipe(skip(2)), | ||
}) | ||
); |
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 can see that the element selector isn't really necessary based on the test's description, but with the removal of the element selectors there are now no tests that specify them. Maybe we should add one?
src/internal/operators/groupBy.ts
Outdated
export function groupBy<T, K, E>( | ||
key: (value: T) => K, | ||
options: GroupByOptionsWithElement<K, E, T> | ||
): OperatorFunction<T, GroupedObservable<K, E>>; | ||
|
||
export function groupBy<T, K, E>( | ||
key: (value: T) => K, | ||
options: GroupByOptionsWithElement<K, E, T> | ||
): OperatorFunction<T, GroupedObservable<K, E>>; |
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.
AFAICT, these signatures are the same?
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.
Oops, I must have refactored one and not noticed.
if (!elementOrOptions || typeof elementOrOptions === 'function') { | ||
element = elementOrOptions; | ||
} else { | ||
({ duration, element, connector } = elementOrOptions); |
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.
Destructuring assignment FTW. 🎉❤
NOTES:
duration
selector that is noted in aTODO
in the code here.groupBy
appear to be EXTREMELY old and outdated, and I was unable to updated them easily to use run mode. We may have to rewrite them all at some point to use better techniques. The issue seems to be a rudementary means of testing the inner observables that is incompatible with run mode.