-
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
chore(typings): reorder project overloads #2855
Conversation
Projection functions can match ObservableInput, so overloads with project parameters are more specific and should precede ObservableInput overloads. Most existing overloads are already ordered that way; there are only two that are not: those for the static combineLatest and zip operators.
Generated by 🚫 dangerJS |
This looks harmless but curious if there's way to test there are no regressions. 🤔 |
It should be harmless. If there is code that's impacted, the change in this PR should do nothing other than make explicit type assertions redundant. For example, without the PR, this code: const ob = Observable.combineLatest<any, any>(
Observable.of(1),
(value: any) => value
); Would see the type of const ob: Observable<any> = Observable.combineLatest<any, any>(
Observable.of(1),
(value: any) => value
); With the change, the assertion would be redundant. A lot of this TypeScript stuff devolves into thought experiments. It's hard to test. I have some ideas about how some test infrastructure could be added to test for TypeScript errors that should be effected, but I've been side-tracked by a particular TypeScript issue. The fact that a function will match |
yeah, as you mentioned most of type validation is based on 'thought process', reason why for me it looks harmless but thinking about sort of test cases. We alreaddy had several cases of multple reviewer thought type doesn't have regression and once it's released found out actual cases we couldn't think of. Especially operator like this have number of overload with specific inference fallback, I'd like to have some sort of guards for these kinds of chnges. just not sure how we can achieve it. we have some level of type definition testing but it is very minimal at this moment. |
Yeah, I understand that it's disconcerting. I will give it some more thought once I've got more information about the function-matching- We'll see, I guess. |
👍 |
@kwonoj I've written tool - The way I see it:
Note that option 4 will still require the My intention is to augment the implementation of the With the snippet compiler and expectations in place, I'll be able to test a fair bit of stuff that's currently untestable and will be able to start addressing some of the typing issues that I've found. |
@cartant we don't have strong policies around devdependencies as long as licenses are allowed, so I don't think that's big concern. It's more interesting what that dependency actually those - in a high level, compare to current |
To clarify, there's nothing wrong with the current The case for testing with snippets is best put using some examples. In #2891, I've added tests like this: type('should infer the type with a type-changing selector', () => {
/* tslint:disable:no-unused-variable */
const source = Rx.Observable.of<number>(1, 2, 3);
const result: Rx.Observable<string> = source.multicast(() => new Rx.Subject<number>(), s => s.map(x => x + '!'));
/* tslint:enable:no-unused-variable */
}); Tests like these attempt verify that the inferred type is correct. For the most part, they work. However, they don't guard against a reversion in which the return type is inferred as, say, If a snippet were to be used instead, such reversions would be detected: type('should infer the type with a selector', (snippet) => {
const s = snippet(`
const source = Rx.Observable.of<number>(1, 2, 3);
const result = source.multicast(() => new Rx.Subject<number>(), s => s.map(x => x + '!'));
`);
s.toInfer("result", "Observable<string>")
}); Snippets are also useful in issues like the one this PR addresses, as the non-snippet That is, this would test next to nothing: type('should infer the correct type', () => {
/* tslint:disable:no-unused-variable */
const result: Rx.Observable<any> = Rx.Observable.combineLatest<any, any>(
Rx.Observable.of(1),
(value: any) => value
);
/* tslint:disable:no-unused-variable */
}); Whereas, this is more useful and would not pass with the current codebase (as the overload signatures need to be re-ordered): type('should infer the correct type', (snippet) => {
const s = snippet(`
const result = Rx.Observable.combineLatest<any, any>(
Rx.Observable.of(1),
(value: any) => value
);
`);
s.toInfer("result", "Observable<any>");
}); To take another example, there is a problem with the overload signatures for the const input: Rx.ObservableInput<string> = Rx.Observable.of<number>(1); At the moment, this test would not pass: type('should not be assignable to observables of incompatible types', (snippet) => {
const s = snippet(`
const input: Rx.ObservableInput<string> = Rx.Observable.of<number>(1);
`);
s.toFail(/not assignable/);
}); So, if the The changes to the import * as tss from 'ts-snippet';
context.type = function (title: string, fn: (snippet?: (code: string) => tss.Expect) => void): void {
if (fn && fn.length === 1) {
function snippet(code: string): tss.Expect {
const s = tss.snippet({
'snippet.ts': `
import * as Rx from './dist/cjs/Rx';
${code}
`
});
return s.expect('snippet.ts');
}
fn(snippet);
} else {
//intentionally does not execute to avoid unexpected side effect occurs by subscription,
//or infinite source. Suffecient to check build time only.
}
}; The actual implementation would be a little more complicated, as I'd like to reuse the compiler - to avoid reading and parsing the |
🤦♂️ |
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
Description:
Most of the TypeScript overloads for operators that take a projection function place the overloads that include projection functions before the overloads that do not. That's the way it should be.
However, there are two that do not:
combineLatest
operator; andzip
operator.The problem is that a projection function can be matched against
ObservableInput<any>
, as any function will matchArrayLike<any>
. That means the overloads that include projection functions are more specific and should be placed first. If they are not, this:will match this:
instead of this:
and the incorrect type will be inferred for the result.
Related issue (if exists): None.