My objective is to utilize an object for rendering a React component based on a data type property. I am exploring ways to avoid utilizing switch cases when narrowing a discriminated union type in TypeScript. The typical method involves using if-else or switch statements, where TS automatically narrows the value:
type AView = {type: 'A', name: string}
type BView = {type: 'B', count: number}
type View = AView | BView
const prints = {
'A': (view: AView) => `this is A: ${view.name}`,
'B': (view: BView) => `this is B. Count: ${view.count}`,
} as const
const outputWorks = (view: View): string => {
switch (view.type) {
case 'A':
// or prints['A'](view)
return prints[view.type](view)
case 'B':
// or prints['B'](view)
return prints[view.type](view)
}
}
outputWorks({type: 'A', name: 'John'})
I am curious whether there is a way to avoid using the switch statement and convince TS that the object can effectively narrow down the data:
type AView = {type: 'A', name: string}
type BView = {type: 'B', count: number}
type View = AView | BView
const prints = {
'A': (view: AView) => `this is A: ${view.name}`,
'B': (view: BView) => `this is B. Count: ${view.count}`,
} as const
const outputFail = (view: View): string => prints[view.type](view)
outputFail({type: 'A', name: 'John'})
When implementing this approach, I encounter the error
The intersection 'AView & BView' was reduced to 'never' because property 'type' has conflicting types in some constituents.
due to TS failing to narrow down the type.
You can view and interact with this code on the TS Playground.
I have been working on resolving this issue for a week now and it seems quite challenging. Despite looking at various related discussions, I haven't been able to solve this problem yet.