Reputation: 1421
I have a discriminated union where the discriminator is an enum and the value of the enum field determines whether or not another field is undefined. As so:
enum Status {
TOP, BOTTOM, MIDDLE
}
type StatusReport =
| {
status: Status.TOP | Status.BOTTOM
message: undefined
}
| {
status: Status.MIDDLE
message: string
}
All fine. Now I introduce a function to build such a type from the appropriate parameters:
function statusReporter(theStatus: Status, statusMessage:string):StatusReport{
return {
status: theStatus,
message: theStatus === Status.MIDDLE ? statusMessage : undefined
}
}
This function logic looks sound to me. It will assign the correct value to message
depending on the value of the enum value being set in status
. But TS errors claiming Status is not assignable to type Status.MIDDLE. That's true, of course, but I believe my conditional is ensuring that won't happen.
Why am I getting this error and what's the fix?
Here's the playground
I've tried asserting the type of theStatus
as a conditional type dependent on whether or not theStatus
extends Status.MIDDLE
(status: theStatus as typeof theStatus extends Status.MIDDLE ? Status.MIDDLE : Status
), but it doesn't help. I get the same error.
Upvotes: 2
Views: 2582
Reputation: 537
You have to determine the type before you create your object.
function statusReporter(theStatus: Status, statusMessage:string):StatusReport{
return theStatus === Status.MIDDLE ? {
status: theStatus,
message: statusMessage
} : {
status: theStatus,
message: undefined
}
}
Upvotes: 1
Reputation: 33051
You need to use function overloading:
enum Status {
TOP, BOTTOM, MIDDLE
}
type WithoutMessage = {
status: Status.TOP | Status.BOTTOM
message: undefined
}
type WithMessage = {
status: Status.MIDDLE
message: string
}
type StatusReport = WithMessage | WithoutMessage
function statusReporter(theStatus: Status.MIDDLE, statusMessage: string): WithMessage
function statusReporter(theStatus: Exclude<Status, Status.MIDDLE>, statusMessage: string): WithoutMessage
function statusReporter(theStatus: Status, statusMessage: string): StatusReport
function statusReporter(theStatus: Status, statusMessage: string) {
return {
status: theStatus,
message: theStatus === Status.MIDDLE ? statusMessage : undefined
}
}
function builder(myStatus: Status) {
return statusReporter(myStatus, "hello");
};
const hof = builder(Status.MIDDLE) // StatusReport
const result = statusReporter(Status.MIDDLE, 'hello') // WithMessage
const result2 = statusReporter(Status.BOTTOM, 'hello') // WithoutMessage
const result3 = statusReporter(Status.TOP, 'hello') // WithoutMessage
You are getting this error because message
property evaluates to string | undefined
while StatusReport
expects either undefined
or message
but not both, I mean not union of them
UPDATE
function statusReporter(theStatus: Status, statusMessage: string): StatusReport {
if (theStatus === Status.MIDDLE) {
return {
status: theStatus,
message: statusMessage
}
}
return {
status: theStatus,
message: undefined
}
}
Upvotes: 1