Suppose you have the following type:
type TComp <T> = (cb: (arg: T) => void, value: T) => void;
and two different 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, let's introduce string identifiers for types:
type TStrNum = 'str'|'num';
type TNameTypeMap = {
'str': string;
'num': number;
}
We want to create a function TComp <T>
with the following conditions:
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`
}
}
When TypeScript encounters //1
, it throws the error:
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'.
Similarly, in //2
, the same issue arises but with string.
This problem seems to be associated with contravariance of cb
, but I am unsure of the exact cause.
Is there a solution to this problem, or perhaps another way to implement this function?