I am attempting to design generic message types that become increasingly specific, with the most precise type being inferred during the function call or class creation.
Consider the following code snippet:
type MessageHeaderType = 'REQUEST'|'RESPONSE'|'UPDATE'|'ERROR'|'EVENT'
type UnknownRecord = object
interface Message<MessageBody = UnknownRecord> {
header: {
id?: string
type: MessageHeaderType
},
body: MessageBody
}
interface MessageUpdate<MessageBody = MessageUpdateBody> extends Message<MessageBody> {
header: {
id: string
type: 'UPDATE'
}
}
interface MessageUpdateBody {
code: string
data?: UnknownRecord
}
This setup works smoothly and allows for easy creation of a MessageUpdate
:
const ExampleMessageUpdate: MessageUpdate = {
header: {
id: '123',
type: 'UPDATE'
},
body: {
code: 'SOME_UPDATE',
data: {
foo: 'bar'
}
}
} // <== All good!
The issue arises when giving users the ability to generate their custom message update body "on the fly" (still at compile-time, obviously!). The code below does not permit indexing the generically passed implementation in a specific MessageUpdate
type:
function createMessage<ThisMessageUpdateBody>(code: string, data?: ThisMessageUpdateBody['data']): MessageUpdate<ThisMessageUpdateBody> {
const message: MessageUpdate<ThisMessageUpdateBody> = {
header: {
id: '123',
type: 'UPDATE'
},
body: {
code,
data
}
}
return message
}
An error is triggered regarding the function parameters:
Type '"data"' cannot be used to index type 'ThisMessageUpdateBody'
Is there a way to further narrow down this type without encountering an error?