-
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
Subject.prototype.next() should only accept T, not T | undefined #2852
Comments
This.. seems makes sense to me? I think it's just kind of legacy when we deal with old version of TSC have limitation with union types. |
/cc @david-driscoll |
Could we get this in before 6.0 as it's a slight breaking change? |
It's breaking anyway, isn't it? |
What's breaking anyway? |
Someone Honestly I'm not very tempted to touch type definition in v5 currently, lot of them are written with old versions of TSC compiler and we need revise huge breaking changes with v6 anyway, also changing v5 signature with old version surprisingly can affect user code in latest TSC. (not saying this one's those case) |
Yeah, that's why I'm asking whether we could get this change to be in 6.0 |
Oh, I thought you're asking it to be in 5.x. my bad. This should be in 6 in any cases. |
Just clarifying lot of v6 issues are backlogged, cause |
Prevents emitting undefined on strictly-typed Subjects BREAKING CHANGE: If you are using TypeScript called next() without a value before, TypeScript will error. If the Subject is of type void, explicitely pass undefined to next(). closes ReactiveX#2852
So, to be clear to others, if someone wants to use it as: |
Hmmm... this is nasty. This effectively means with TS you would never be able to call |
I agree it is not nice, even though I personally would still prefer that over the risk of someone calling it without a value and therfor emitting I tried to overload I think this is the TypeScript issue that would make this possible: microsoft/TypeScript#12400 |
The current situation in v6.x is still very unexpected to me as a TypeScript developer when using the const someSubject = new Subject<string[]>();
// will throw an error when s is undefined!
// Worse: The type of arr is inferred as string[], but it
// actually should have been (string[] | undefined)
someSubject.subscribe(arr => arr.forEach(s => console.info(s));
// this line should be a compile error in --strict mode
someSubject.next(); rxjs Subjects will use the generic type argument and make it nullable: The fact that I always have to pass function print<T>(t: T) {
console.info(t);
}
// this is a compiler error
print<void>();
// this is ok
print<void>(undefined); I don't think rxjs should do some "magic" conversion of Even worse is that the typing of the const someSubject = new Subject<string[]>();
someSubject.subscribe(arr => {
// assuming arr will be inferred to (string[] | undefined) then
// tsc will error here, saying that arr is possibly 'undefined'
arr.forEach(s => console.info(s)
}); Please reconsider changing this. This change affects only users which enable strict nullchecks, that option is off by default and when off still allows me to call .next() without passing |
I find it very problematic that the type signature of the observable does not match up with what you're allowed to |
It's entirely possible that this could be supported via some sort conditional types, I haven't investigated it yet. I'll see if I can this afternoon (I have a free hour or two which is unusual). However the |
Sadly this cannot be done, I hit a hard wall where conditional types are not allows to be used on anything by a I was able to model out the types just fine, however once you implement both the We could maybe work around this by having a factory function to create subjects that returns a subject that implements the correct export interface NextVoidObserver {
closed?: boolean;
next: () => void;
error?: (err: any) => void;
complete?: () => void;
}
export interface NextValueObserver<T> {
closed?: boolean;
next: (value: T) => void;
error?: (err: any) => void;
complete?: () => void;
}
export interface ErrorVoidObserver {
closed?: boolean;
next?: () => void;
error: (err: any) => void;
complete?: () => void;
}
export interface ErrorValueObserver<T> {
closed?: boolean;
next?: (value: T) => void;
error: (err: any) => void;
complete?: () => void;
}
export interface CompletionVoidObserver {
closed?: boolean;
next?: () => void;
error?: (err: any) => void;
complete: () => void;
}
export interface CompletionValueObserver<T> {
closed?: boolean;
next?: (value: T) => void;
error?: (err: any) => void;
complete: () => void;
}
export type PartialVoidObserver = NextVoidObserver | ErrorVoidObserver | CompletionVoidObserver;
export type PartialValueObserver<T> = NextValueObserver<T> | ErrorValueObserver<T> | CompletionValueObserver<T>;
export type PartialObserver<T> = PartialVoidObserver | PartialValueObserver<T>;
export interface VoidObserver {
closed?: boolean;
next: () => void;
error: (err: any) => void;
complete: () => void;
}
export interface ValueObserver<T> {
closed?: boolean;
next: (value: T) => void;
error: (err: any) => void;
complete: () => void;
}
export type Observer<T> = T extends void ? VoidObserver : ValueObserver<T>; However you should be able to cast (or assign) a subject to one of these interfaces as the declaration should match either or, but you would have to add the interface to your code or a shared library you control. |
Sounds like holding out for microsoft/TypeScript#12400 is still the best option, then. Thanks for the attempt. |
This is huge! I just found this out. We have huge codebase and now we have to question undefined checking every single Observable with a Subject source and every angular output ( |
I think we can solve this with tuple types: interface Subject<T> {
next(...args: T extends void ? [] | [T] : [T]): void
} |
Nice. That looks like a good solution, to me. |
you can also do this: interface Subject<T> {
next(this: Subject<void>): void;
next(arg: T): void;
} But anyways, typescript already consideres interface Subject<T> {
next(...args: T extends void ? [] | [T] : [T]): void
} is equivalent to interface Subject<T> {
next(arg: T): void;
} |
Closing this because in v7 Lines 54 to 62 in 4805a7f
|
Hey folks, looks like new Observable<string>(subscriber => {
subscriber.next() // should throw in compile-time!
}) cc @cartant |
RxJS version: 5.4.3
Code to reproduce:
Expected behavior:
Compile error
Actual behavior:
Compiles
Additional information:
next()
's argument is typed as optional, which means it allowsundefined
even if the Subject's type does not. It should only allowT
, notT | undefined
.The text was updated successfully, but these errors were encountered: