In typescript I know that for type safety when you have a union of functions, if you want to call this function, you have to pass it a intersection of its parameters instead of an union, but this behavior can be annoying when you already have type checks that ensure that no matter what function of the union you'll call, you will always pass it the right parameters even when passing an union instead of passing every possible parameters. Let me explain with a small example:
type Runner = {
    a: () => void,
    b: (param: { x: number }) => void,
    c: (param: { y: string, z: number }) => string,
};
type Action = {
  [K in keyof Runner]: { kind: K, param: Runner[K] extends () => any ? unknown : Parameters<Runner[K]>[0] }
}[keyof Runner];
const runner: Runner = {
    a: () => {},
    b: ({ x }) => {},
    c: ({ y, z }) => "c",
};
const action = { kind: "c", param: { y: "5", z: 5 } } as Action;
runner[action.kind](action.param);
Here we can see that the param field is tied to the kind field which is every field of Runner, so every possible function of the union, so we know that we will have the right parameter depending on the kind field, but still typescript complains because you can't call a union of function with a union of parameters. Is it possible to handle these kind of cases nicely without having to add any casts into the mix ?
Not that I know of. It's a limitation I've encountered many times myself. In the end just had to structure my code differently.
Typescript treats both fields of the action as all possible values of the union, until you give it smth to discriminate it by and narrow the type.
const action = { kind: "c", param: { y: "5", z: 5 } } as Action;
runner[action.kind](action.param); // error
if (action.kind === "c") {
    runner[action.kind](action.param); // OK, action is narrowed to one member of the union
}
const action2:Action = { kind: "c", param: { y: "5", z: 5 } };
runner[action2.kind](action2.param); // OK, action2 is narrowed to one member of the union
This might improve in the future, as they constantly refine the control flow analysis, but for now, you have to either give TS a way to discriminate the union or cast it. I try to restructure my code to avoid casting, since casting in TS is nothing more than just shutting the compiler up, but if I have to I do it in the isolated function and then cover it with unit tests extensively.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With