Imagine having a union type called Action
, which is discriminated on a single field @type
, defined as follows:
interface Sum {
'@type': 'sum'
a: number
b: number
}
interface Square {
'@type': 'square'
n: number
}
type Action = Sum | Square
Now, let's create an object with methods for each Action
using key remapping:
const handlers: {[A in Action as A['@type']]: (action: A) => number} = {
sum: ({a, b}) => a + b,
square: ({n}) => n ** 2
}
Is there a safe way to call one of these handlers by passing the Action
as an argument? The goal is to ensure type safety in the following code snippet:
const runAction = (action: Action) => {
return handlers[action['@type']](action) // <-- Not valid
}
However, TypeScript throws an error stating:
Argument of type 'Action' is not assignable to parameter of type 'never'.
The intersection 'Sum & Square' was reduced to 'never' because property ''@type'' has conflicting types in some constituents.
Type 'Sum' is not assignable to type 'never'.
What would be a proper way to rewrite the above function in a safe manner without sacrificing strict typing?