-
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(tap): add assertion function overload #7272
Conversation
ensure input type when throwing conditionally in `next` of `tap`
I think |
I see. |
In this case, it turns out that you will have to do overloads in all operators that have a callback. It will be much easier to just make an alias on a specific project: function ensure<T, R extends T>(
next: (value: T) => asserts value is R
): OperatorFunction<T, R> {
return tap({ next }) as OperatorFunction<T, R>;
} const stringOrNumberObservable = of(1, 'aaa', 3, 'bb');
stringOrNumberObservable
.pipe(
ensure((x): asserts x is string => {
if (typeof x !== 'string') {
throw new Error('not a String');
}
})
)
.subscribe((s) => s.length); // s is {string} |
To be honest, I don't see a compelling reason to add such functionality to RxJS. The purpose of the asserts keyword is tied to local control flow when writing imperative code. Essentially, what asserts does is signal that the next computation will not execute if the variable is not of the expected type. For instance: function assertIsString(value: unknown): asserts value is string {
if (typeof value !== 'string') throw Error('value is not a string');
}
const x = 5;
assertIsString(x);
// This code is unreachable
console.log(x) That is the only valid use case. In functional programming, different control flows exist within different functions, hence The primary advantage of the asserts keyword is its influence on TypeScript's control flow analysis, which is particularly useful in imperative code, but in the context of RxJS and reactive programming, this approach isn't as directly applicable. |
Thank you so much for the work, @Milly! This is an interesting idea, but I tend to agree with the statements above. Assertions like this are generally a part of imperative control flow... Hover, using that same concept, you can leverage declare const source$: Observable<any>;
const result: Observable<string> = source$.pipe(
map(x => {
if (typeof x !== 'string') {
throw new TypeError('value must be a string!');
}
return x;
})
); |
Also, @Milly, if you disagree with this (and I know it took me forever to respond), please feel free to post concerns or whatever here. An |
Description:
I think no breaking changes in this PR.
Specifying an assertion function to
tap()
will ensure the type that follows.This is similar to specifying a type guard function to
filter()
.Related issue (if exists):
Nothing.