Home > Blockchain >  Contravariance problem in generic function
Contravariance problem in generic function

Time:10-23

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            
  • Related