Create an AbstractSubject class with two type parameters as a base for the Subject class to permit other subjects that mutate messages.
Feature Request: AbstractSubject
The Subject class is an excellent pattern for two-way communication. Some examples that spring to mind are web workers, web sockets, long polling, and inter-component messaging. One end calls subscribe to receive messages from the other end, and calls next to send messages to the other end. The other end does the same thing.
A hypothetical MessageSubject would extend Subject, and override the constructor or subscribe method to manage an internal connection to the other end. It would listen for incoming messages and pass them on to super.next.
It would also override the next method, and instead of calling every observer.next with the value, it would call the appropriate postMessage method of its internal connection.
You then have an easy two-way communication manager with all the benefits of Observable and pipe. The subscribe, unsubscribe, and complete methods can manage the connection transparently.
It works great. The messaging is intuitive, and the details of the connection are opaque. The web socket opens when there are subscriptions, and closes when there are none.
However, for this model to be robust, messages that are sent need to be able to differ in type from the messages received. This has been recognized by others implementing the same pattern in a similar fashion: #5381: WebSocketSubject may have different input and output types and #5390: feat(FrankenSubject): adds type.
This can be implemented in a new class by adding second type parameter:
export class MessageSubject<S, T> extends Subject<T> {
...
next(value: S) {
// New code to send message to other thread.
}
}
But unfortunately, a compiler error is generated:
Property 'next' in type 'MessageSubject<S, T>' is not assignable to the same property in base type 'Subject<T>'.
This necessitates a // @ts-ignore comment to be associated with the next declaration. This works, but is a bit of an abomination.
I would like to see RXJS implement an abstract subject class with two type parameters like so:
export abstract class AbstractSubject<S, T> extends Observable<T> implements SubscriptionLike {
// Everything currently in the Subject class except the next method goes in here.
// Declare the next method as abstract, using the new type parameter.
abstract next(value: S): void
}
Then Subject would simply extend this abstract class with the single type parameter for both, and define the contents of the next method as it currently does:
export class Subject<T> extends AbstractSubject<T, T> {
next(value: T): {
// The current next code goes here.
}
}
The new MessageSubject would extend the AbstractSubject directly, implementing the next method as needed instead of overriding.
An AbstractSubject would allow for subjects that mutate messages: what goes in is not necessarily what comes out. Nor is the rate the same, the source for messages coming in is not tied to what is sending them. It would allow two-way communication between different threads using a familiar pattern with the details of the connection hidden. I think it opens enough interesting possibilities to be worth what I believe to be a ~~trivial~~ alteration. Except the documentation.
I am certainly willing to do the work if this request is entertained. I have a work-around with //@ts-ignore but obviously that is not pretty nor conducive to others exploring the subject pattern in novel ways.
EDIT: AnonymousSubject throws a wrench in the works. Looking.
I vote for it.
I have something similar and I call it IOSubject. It is very similar toWebSocketSubject and I use IOSubject to abstract WebWorker, MessageChannel, WebSocket and Socket.IO.