Snippet and error:
const methods = {
a(value: number) {},
b(value: string) {}
};
function callMethodWithArg(methodAndArg: { method: 'a'; arg: number; } | { method: 'b'; arg: string; }) {
methods[methodAndArg.method](methodAndArg.arg);
}
Argument of type 'string | number' is not assignable to parameter of type 'never'. Type 'string' is not assignable to type 'never'.
Looks like typescript isn't intelligent enough to figure out that method a can only be called with a number and method b can only be called with a string.
Any suggestions how to type this properly?
CodePudding user response:
I'm not sure if there is a cleaner solution, but this will work if you don't have too many cases:
const methods = {
a(value: number) {},
b(value: string) {}
};
function callMethodWithArg(methodAndArg: { method: 'a'; arg: number; } | { method: 'b'; arg: string; }) {
if (methodAndArg.method === 'a') {
// now it knows that method has to be 'a' and arg is a number
methods[methodAndArg.method](methodAndArg.arg)
} else if (methodAndArg.method === 'b') {
methods[methodAndArg.method](methodAndArg.arg)
}
}
CodePudding user response:
My solution would be to use Type Predicates to cast the method as either Shape A or B (or however many shapes you need)
type MethodA = (value: number) => void
type MethodB = (value: string) => void
type ArgA = { method: 'a'; arg: number; }
type ArgB = { method: 'b'; arg: string; }
type Methods = {
[key: string]: MethodA | MethodB
}
const methods: Methods = {
a(value: number) { },
b(value: string) { }
};
function callMethodWithArg(methodAndArg: ArgA | ArgB) {
const method = methods[methodAndArg.method]
if (determinShape(methodAndArg)) {
(method as MethodA)(methodAndArg.arg)
} else {
(method as MethodB)(methodAndArg.arg)
}
}
/** Will cast methodAndArg as ArgA if true, else ArgB */
function determinShape(methodAndArg: ArgA | ArgB): methodAndArg is ArgA {
return Number.isFinite(methodAndArg.arg)
}
