I've been exposed to numerous tricks, but I seem to be struggling with this particular puzzle; therefore, any assistance from someone with more experience in TS would be greatly appreciated.
My subscribe()
function requires:
- Message type in the form of a string
- The generic type parameters within the given Message type
In simpler terms, this is what I aim to achieve:
messaging.subscribe<NameAgeMessage>(
(message) => {
console.log(`received message type '${message.messageType}', with id=${message.msg.id}`);
});
However, I'm currently dealing with:
messaging.subscribe<NameAgeMsg, NameAgeSatisfyArg>(
NameAgeMessage.MESSAGE_TYPE,
(message) => {
console.log(`received message type '${message.messageType}', with id=${message.msg.id}`);
});
This is due to my struggle with understanding method signatures and type nuances:
public subscribe<TMsg, TSatisfyArg>(
messageType: string,
observer: (message: AppMessage<TMsg, TSatisfyArg>) => void): Subscription {
const messagesFiltered = this.messages
.pipe(filter((message) => message.messageType === messageType));
return messagesFiltered.subscribe(observer as any);
}
If you wish to explore alternative syntax, check out the lower section of this code scaffold:
(Link to Playground for Code Execution)
import { BehaviorSubject, Subscription, filter } from "rxjs";
abstract class AppMessage<TMsg, TSatisfyArg> {
abstract readonly messageType: string;
abstract readonly msg: TMsg;
satisfy!: undefined | ((msg: TMsg, satisfyArg: TSatisfyArg) => void);
protected constructor() { }
};
class MessagingServiceStartupMessage extends AppMessage<undefined, undefined> {
public static readonly MESSAGE_TYPE: string = 'message-service-startup-message';
public readonly messageType = MessagingServiceStartupMessage.MESSAGE_TYPE;
constructor(
public readonly msg: undefined
) {
super();
}
}
type NameAgeMsg = { id: number };
type NameAgeSatisfyArg = { name: string, age: number };
class NameAgeMessage extends AppMessage<NameAgeMsg, NameAgeSatisfyArg> {
public static readonly MESSAGE_TYPE: string = 'name-age-message';
public readonly messageType = NameAgeMessage.MESSAGE_TYPE;
constructor(
public readonly msg: NameAgeMsg
) {
super();
}
}
class MessagingService {
private messages: BehaviorSubject<AppMessage<any, any>> = new BehaviorSubject<AppMessage<any, any>>(
new MessagingServiceStartupMessage(undefined));
public publishForSatisfy<TMsg, TSatisfyArg>(
message: AppMessage<TMsg, TSatisfyArg>,
satisfy: (msg: TMsg, satisfyArg: TSatisfyArg) => void
): void {
message.satisfy = satisfy;
}
// Needs:
// 1. Message type as string
// 2. The generic type parameters in the provided Message type
public subscribe<TMsg, TSatisfyArg>(
messageType: string,
observer: (message: AppMessage<TMsg, TSatisfyArg>) => void): Subscription {
const messagesFiltered = this.messages
.pipe(filter((message) => message.messageType === messageType));
return messagesFiltered.subscribe(observer as any);
}
}
class TestClass {
messaging: MessagingService = new MessagingService();
testSubscribe(): void {
// QUESTION: Can this be cleaned up...
this.messaging.subscribe<NameAgeMsg, NameAgeSatisfyArg>(
NameAgeMessage.MESSAGE_TYPE,
(message) => {
console.log(`received message type '${message.messageType}', with id=${message.msg.id}`);
});
// ...to be something like this?
this.messaging.subscribe<NameAgeMessage>(
(message) => {
console.log(`received message type '${message.messageType}', with id=${message.msg.id}`);
});
}
testPublish(): void {
this.messaging.publishForSatisfy(
new NameAgeMessage({ id: 123 }),
(msg, satisfyArg) => {
});
}
}