I am attempting to use a type guard to narrow down a complex type. In my scenario, I want the false branch of the type guard to recognize the complement of the narrowed type.
interface Model { side: 'left' | 'right'; }
interface LeftModel { side: 'left'; }
interface RightModel { side: 'right'; }
type Either = LeftModel | RightModel;
function isLeft(value: Either): value is LeftModel { // else is RightModel
return value.side === 'left';
}
It seems that achieving this is not feasible with my current approach. While TypeScript can infer that an Either
may be a model, it does not accept that a Model
can be an Either
. This results in an error:
declare const model: Model;
isLeft(model) // ts(2345)
Is there no solution to this issue?
If so, how can I ensure that the false branch narrows down to the complement?
View the complete example in this Typescript Playground
UPDATE
In this basic example, it appears that Model
and Either
are interchangeable. However, this might not always hold true. I tried merging two type guards to inform the type system that Model
is indeed a valid Either
(see this new Playground). However, this led to an unwanted branch (refer to line 22), making it less than ideal.
Is there a way to convince the type system that Either
and Model
are essentially the same?
I do not necessarily need to rely on type guards or union types as my initial attempt raised its own issues. Union types would only work if we could guarantee that the union of a narrowed type and its relative complement aligns with the narrowed type. This assumption relies on the type system recognizing the concept of complement, which may not currently be the case. Refer to this typescript complement search and the handbook on utility types.
Someone recommended utilizing fp-ts and/or monocle-ts to address this issue, but some of these functional programming concepts are still beyond my grasp. If someone knows how to apply them here, it would be greatly appreciated. Either seems like a possible solution...