Consider the following type:
type TComp <T> = (cb: (arg: T) => void, value: T) => void;
and two implementations of this type:
const f1: TComp<number> = (cb: (a: number) => void, value: number) => {
cb(value 122);
}
const f2: TComp<string> = (cb: (a: string) => void, value: string) => {
cb(value "ok");
}
Now I introduce string names for types:
type TStrNum = 'str'|'num';
type TNameTypeMap = {
'str': string;
'num': number;
}
Now I want to construct function, TComp <T>
such that:
const sf = <T extends TStrNum>(cb: (a: TNameTypeMap[T]) => void, value: TNameTypeMap[T], name: TStrNum) => {
if(name==='num') {
return f1(cb, value); //1 ts complains on `cb`
}
if(name==='str') {
return f2(cb, value); //2 ts complains on `cb`
}
}
in //1
TypeScript complains:
Argument of type '(a: TNameTypeMap[T]) => void' is not assignable to parameter of type '(arg: number) => void'.
Types of parameters 'a' and 'arg' are incompatible.
Type 'number' is not assignable to type 'TNameTypeMap[T]'.
Type 'number' is not assignable to type 'never'.
in //2
the same error, only complaining on string
Looks like this problem is related to contravariance of cb
, but I still cannot figure out what exactly is wrong.
Is it possible to fix it or implement this function in some other way?
CodePudding user response:
Before we worry about the implementation, you first should consider a problem from the caller's side of the function. By typing the name
parameter as TStrNum
you are not requiring that the name
parameter be correlated with the value
and cb
parameters, so you could make the following call without the compiler complaining:
sfOrig<"num">((a: number) => a.toFixed(), 123, "str"); // no compiler error,
// RUNTIME