I'm currently working on creating a message subscription function. A basic version without types is shown below:
function createMessage(message) {
postMessage(message)
}
function addSubscriber(messageType, callback) {
handleNewMessage(message => {
if (message.type === messageType) {
callback(message.data)
}
})
}
The createMessage
function can support multiple message types. To achieve this, I define each type separately and then use a discriminated union to type the message
argument. Example:
type InfoMessage = {
type: 'info',
data: {
x: string,
y: number
}
}
type ErrorMessage = {
type: 'error'
}
type MessageType = InfoMessage | ErrorMessage
Notice how ErrorMessage
does not have any associated data.
This allows me to type the createMessage
function as follows:
function createMessage(message:MessageType):void {
postMessage(message)
}
However, I am having trouble figuring out the correct typing for the addSubscriber
function. Here's what I have so far:
function addSubscriber(messageType: MessageType['type'], callback) {
handleNewMessage(message => {
if (message.type === messageType) {
callback(message.data)
}
})
}
I am uncertain about how to properly type the callback
. Using MessageType['data']
generates an error since data
may not always be present. Even adding data:undefined
to ErrorMessage
results in losing the connection between the message type and its data.
My ideal scenario would involve writing
addSubscriber('error', (data) => console.log(data))
with TypeScript recognizing that data
is actually undefined in this context.
One approach I've considered is declaring the function type individually for each message type, but this seems cumbersome as it requires defining both the MessageType
and the corresponding subscribe function for each type of message.
In practice, I have numerous messages and would prefer to avoid this repetitive process.
What would be the most effective way to type the addSubscriber
's callback
function?