Home > other >  Generic Function and index access
Generic Function and index access

Time:10-20

I've encountered a problem regarding typescript and need some help

I'm new to typescript and a code block below illustrates shortened version of my problem.

Link to Typescript Playground

interface A  {
    value1: string;
    value2: string;
    func(a: string, b: string): void;
}

interface B {
    value1: number;
    value2: number;
    func(a: number, b: number): void; 
}


function Outer <T extends A | B>(myFunc: T['func'], a: T['value1'], b: T['value2'])  {
    myFunc(a, b); // get an error: Argument of type 'string | number' is not assignable to parameter of type 'never'.
}

What I want is to pass parameters to 'Outer' function which will then infer 'T'.

I thought it would be okay to call 'myFunc', but error comes up and says I can't assign 'a' and 'b' to 'never'.

** This set of codes might look a bit weird. It's actually intended to be a react hook.

What's going on here? and What would be a proper way to solve this?

Thanks a lot in advance.

CodePudding user response:

This is because of union to intersection behavior of typescript. Normally when the function Outer is called, it is not clear, what object type(A or B) is passed to it, so typescript considers the parameters of the func function in the form of the intersected types.

// it is not obvious that myFunc is this ? :
func(a: string, b: string): void;


//or this? :
func(a: number, b: number): void;


// so typescript changes it like this :
func(a:number & string, b: number & string) = func(a:never, b:never)

//since number set and string set do not have any intersection, the types are never.

What you can do is to consider separate types for the properties of each interface so that their intersection is an existing type.

type AValue =  {a1:string; a2:string}

interface A {
    value: AValue;
    func(x:AValue): void; 
}

type BValue =  {b1:number; b2:number}

interface B {
    value: BValue;
    func(x:BValue): void; 
}


function Outer <T extends A | B>(myFunc: T['func'], a: AValue & BValue)  {
    myFunc(a);
}

You can read more about union to intersection in typescript here. Although it might be better to change the Outer function declaration and solve the problem in a different way so that this union to intersecton thing does not happen.

CodePudding user response:

This particular example can be solved in a more elegant way

Typescritp playground

function Outer<T>(myFunc: (a: T, b: T) => void, a: T, b: T)  {
    myFunc(a, b);
}

// example calls
Outer<number>((a,b) => { console.log( `a b= ${a b}` )},  6, 8);

Outer<string>((a,b) => { console.log( `a=${a} b=${b}` )},  "aa", "bbb");

// You can eve omit the T, since it will imply it for the values of 2nd nad the third value
// In this cast it is a date
Outer((a,b) => { console.log( `a=${a.toLocaleString()} b=${b.toLocaleDateString()}}` )},  new Date(), new Date("2017-01-01"));
  • Related