Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript: function return type depending on input function returns type

I have a function that takes another function in parameters. I would like to return a generic interface configured by the return type of the function passed in parameters.

function doSomething <T>(values: Whatever[], getter: (whatever: Whatever) => T): T[] {
  return values.map(value => getter(value));
}

Then I wanted to make the getter function optional and use a default value for this one. The problem happened at that time.

function doSomething <T>(values: Whatever[], getter: (whatever: Whatever) => T = val => val): T[] {
  return values.map(value => getter(value));
}

So now I'm getting and error saying :

Error:(18, 47) TS2322: Type '(val: Whatever) => Whatever' is not assignable to type '(whatever: Whatever) => T'. Type 'Whatever' is not assignable to type 'T'.

Do you have any idea why I'm getting that error ?

Thank you in advance,

(The example below in not my real code but this is more clear for describe my problem)

I'm using typescript 2.7.2

like image 863
X4V18 Avatar asked Aug 07 '26 21:08

X4V18


1 Answers

The problem is that return type of default getter function is Whatever, but doSomething declaration requires that getter must return T. T is generic type parameter and it could be anything, it's not guaranteed that Whatever will be compatible with T. TypeScript compiler does not see that when default value is provided, T is not necessary and the return type of doSomething is Whatever[]. But you can express it with these overload declarations for doSomething:

function doSomething<T>(values: Whatever[], getter: (whatever: Whatever) => T): T[];
function doSomething(values: Whatever[]): Whatever[];
// the implementation must be compatible with both variants
function doSomething<T>(values: Whatever[], getter: (whatever: Whatever) => T | Whatever = val => val ): (T | Whatever)[] {
  return values.map(value => getter(value));
}

Update to address clarified question:

I wanted to avoid to return "Whatever | T" because everytime I will call this function I will have to check the response type (Whatever or T).

When you call this function, only two overloaded signatures are taken into account, TypeScript does not use implementation signature when doing overload resolution at call sites of doSomething(). In fact, implementation return type could simply be declared as any, as it's done in the overload documentation examples - it's used for type-checking the implementation only, and the implementation is often obvious enough so stricter types do not provide much benefit there.

I would like to write code to take the return type on the getter function and use as T.

If you omit generic argument when calling doSomething, the compiler will infer T from the getter return type. I think the following example does what you want:

interface Whatever { w: string };

function doSomething<T>(values: Whatever[], getter: (whatever: Whatever) => T): T[];
function doSomething(values: Whatever[]): Whatever[];
function doSomething<T>(values: Whatever[], getter: (whatever: Whatever) => T | Whatever = val => val ): (T | Whatever)[] {
  return values.map(value => getter(value));
}

function example() {
    const getter1 = (whatever: Whatever) => whatever.w; // returns string
    const getter2 = (whatever: Whatever) => whatever.w.length; // returns number

    const values: Whatever[] = [];

    const r0 = doSomething(values); // const r1: Whatever[]
    const r1 = doSomething(values, getter1); // const r1: string[]
    const r2 = doSomething(values, getter2); // const r2: number[]
}
like image 200
artem Avatar answered Aug 09 '26 12:08

artem



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!