-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Premature narrowing causing type error #33597
Comments
The declaration of declare function of<A>(...args: A[]): Observable<ValueFromArray<A[]>>; I think at one point we were going to make the form as written illegal since it doesn't really make any sense (the rest arg type is not truly specializable) |
Please don't =( Sometimes, we do want it to infer a tuple type for the type parameter! The order of the input arguments is important. For example, a personal use case, The order of items selected is important and I want Order and length of the tuple* Like, you can't union two The |
While that works, it would be nice to get the specific args from the invocation, in RxJS this would allow the piped of(true, 'test').pipe(
map(callback)
)
// callback has type
type Callback = <T>(elem: Boolean, index: 0): T | <T>(elem: string, index: 1): T It would also allow us to keep track of how large the Observable is so that something like of(1, 2).pipe(take(4)) Can fail at compile time. Is there a way to get that behavior and not have the widening issue mentioned above? |
This isn't really a question and is more of a bug report / feature request (or removal of the "feature" request) since at the moment having rest args extending |
Agreed. It’s also the only way to capture parameter names when writing a function whose sole purpose is to forward a call, a la |
@RyanCavanaugh I guess I'm confused a little by your responses in here. This next release of RxJS is a bid to try to fix all of our current typings woes for our users. I really want to get this right. Would it be possible to collaborate directly with the TS team to shore this up and make sure we don't have regressions when there are new versions of TypeScript? I think that RxJS just happens to trip all of your edge cases. Google catches a lot of those, but the feedback loop is slow: 1) TypeScript releases new major, 2) Google syncs TS into google3 and sees breaking things 3) RxJS team hears about it and investigates 4) Feedback given to TS team. It would be nice to get things straightened out together if possible, even in an advisory capacity, and then work on something where maybe TS team can run dtslint tests that match features we're using so we prevent regressions or at least know more about breaking changes up front. |
Somewhat related question, how big/widely used must a library be before they receive special attention? |
@benlesh RxJS is already a part of our user test suite, so we're basically aware of the changes at the point that they happen (assuming they affect compilation of the library itself). Conversely, our nightlies are available on a nightly basis, so if you'd like to field things from your side, that avenue is available. A bug report with "xxx change breaks RxJs in yyyy scenario" is something we'd treat as very actionable as long as the relevant definitions from the .d.ts were inlined so we can comprehend what's happening without hours of digging. Edit to add: Do we need some representative RxJS-using project in the suite? Is one available? For this specific case, we could work from a more demonstrative example to figure out what your use cases are and determine the best path going forward. Striking the balance between a representative repro and something simplified to the point that it looks like a question/misunderstanding is difficult, so I appreciate the patience and engagement on that front.
"Special attention" is probably overly broad, but any notable project that is something we can build as part of our user test suite is welcome to add themselves. The line for inclusion there is basically "yeah, we've heard of that one". When we do encounter a potential break in those suites, the question is more about representativeness (i.e. is this form of break going to be commonly observed) than popularity, though popularity itself is a de facto form of representation due to usage. |
RxJS includes a number of APIs - // A pretend Observable
class Observable<T> {
constructor(public value: T) {}
}
const init = { type: "init" as const };
const nav = { type: "navigate" as const }; If declare function of<T>(...args: T[]): Observable<T>;
const actions = of(init, nav); // ERROR: '"navigate"' is not assignable to type '"init"' So type ValueFromArray<T extends any[]> = T extends Array<infer U> ? U : never;
declare function of<T extends any[]>(
...args: T
): Observable<ValueFromArray<T>>;
const actions = of(init, nav); Which works fine, except in the situation mentioned above, in which literal arguments are widened. For me, there are two issues:
The latter of these is what concerns me most, as all of the N-args support (i.e. a single, rest-parameter signature instead of multiple overload signatures, each with a different number of parameters) in RxJS is predicated upon this form being supported. |
This issue has been marked as 'Question' and has seen no recent activity. It has been automatically closed for house-keeping purposes. If you're still waiting on a response, questions are usually better suited to stackoverflow. |
Seems this was closed prematurely, it was a little heavier than a simple question, and I don't see a resolution. cc/ @RyanCavanaugh We're still uncertain what the recommended solution should be for these scenarios. |
@RyanCavanaugh Friendly ping on this issue. My teammate ran into this specific issue when trying to create a custom createSelector function function createSelector<T extends readonly any[], R>(
selectors: T,
mapper: (...ts: T) => R
) {
return mapper(...selectors);
}
const works = [1, 'str', true] as const;
createSelector(works, (one, str, bool) => { return {one, str, bool} })
const broken = [1, 'str', true];
createSelector(broken, (one, str, bool) => { return {one, str, bool} })
// also broken
createSelector([1, 'str', true], (one, str, bool) => { return {one, str, bool} }) |
TypeScript Version: 3.6
Search Terms: narrowing generic
When inferring/extracting the array type from an inferred rest arg, it seems that the rest arg is widened which causes the widened type to get passed down to the type and may be too wide to match the declared type.
In the repro case below, the rest type is being inferred as
[number]
instead of[1]
this causes theValueFromArray
to returnnumber
and when attempting to assignnumber
toNum
fails sincenumber
is much larger thanNum
Code
Expected behavior:
const broken: Observable<Num> = of(1)
should compile correctlyActual behavior:
Type error: Type
'Observable<number>' is not assignable to type 'Observable<Num>'.
Playground Link
Related Issues: ReactiveX/rxjs#5027
The text was updated successfully, but these errors were encountered: