Typescript: Wrong return type inference in a generic function using Promise.all

Created on 28 Mar 2020  ·  10Comments  ·  Source: microsoft/TypeScript

Can I resolve this problem below without the awaited keyword? how can I infer return type without having to use as unknown as R[] and providing a hardcoded type each time?

    // Promise.all(this.fork) take a T1[] where T1 is a Promise<number>
    // and return a Promise<T2[]> where T2 is a number
    // therefore T1 and T2 are not both «T»

TypeScript Version: 3.9.0-dev.20200326


Search Terms:
Promise.all
promise.all generic
generic awaited
awaied

Code

class MyMaybeList<T = any>, le 2020-03-24 à 03 45 35 EST

Code as text below -- Click to enlarge image -- #Pop N' Lock Theme by Luxcium ✨

export class MyMaybeList<T = any> {
  private constructor(values: T[]) {
    this.values = values;
  }

  private values: T[];

  // get =======================================================-| fork() |-====

  public get fork(): T[] {
    return this.values != null ? this.values.slice() : [].slice();
  }

  // public =====================================================-| map() |-====

  public map<R = any>(
    fn: (val: T, index: number, array: T[]) => R
  ): MyMaybeList<R> {
    return MyMaybeList.of(...this.values.map(fn));
  }

  // static ======================================================-| of() |-====

  public static of<TVal>(...val: TVal[]): MyMaybeList<TVal> {
    if (val == null || !val.length) return new MyMaybeList<TVal>([]);
    return new MyMaybeList<TVal>([...val]);
  }

  // async =====================================================-| will() |-====

  public async will /* <R> */() /* : Promise<MyMaybeList<R>> */ {
    // Promise.all(this.fork) take a T1[] where T1 is Promise<number>
    // and return a Promise<T2[]> where T2 is a number
    // therefore T1 and T2 are not both «T»
    console.log(this.fork);

    const willThen = Promise.all(this.fork);

    const thenWill = await willThen;
    return MyMaybeList.of(...thenWill);
  }
}

Example, le 2020-03-24 à 03 46 09 EST

Code as text below -- Click to enlarge image -- #Pop N' Lock Theme by Luxcium ✨

const oneToTen = MyMaybeList.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
const powersOneToTen = oneToTen.map(async val => val * val);

// "log->" powersOneToTen: MyMaybeList<Promise<number>>
console.log(powersOneToTen);

// "log->" awaitedList: MyMaybeList<Promise<number>>
// instead of a -> MyMaybeList { Promise<number> }
// in fact is a -> Promise {  MyMaybeList<number> }
const awaitedPowersOneToTen = powersOneToTen.will /* <unnknow> */();
awaitedPowersOneToTen.then(awaitedList => console.log(awaitedList));

// Promise { <pending> } will resolve into ->
console.log(awaitedPowersOneToTen);

Console logs le 2020-03-24 à 03 46 58 EST

Click to enlarge image -- #Pop N' Lock Theme by Luxcium ✨

Now that #37610 has reverted the awaited type I don't know what to do I have provided an example above but my real-life situation is as follow :

I was relying on this solution because I have a Promise<MaybeList<Promise<ISymbolSearchResult[]>>>
where MaybeList is an array abstraction so I am using promise.all to remove the inner Promise I don't know what to do now to avoid using «as unknown as MaybeList<ISymbolSearchResult[]>» inside of my async function (which should be returning the Promise<MaybeList<ISymbolSearchResult[]>> instead of the Promise<MaybeList<Promise<ISymbolSearchResult[]>>>) *Note that I have an array of ISymbolSearchResult inside an arrayLike of type MaybeList so, in this case, an array inside of an array

Actual behavior:
Promise<MyMaybeList<Promise<number>>> (as in 3.9.0-dev.20200326)

Expected behavior:
Promise<MyMaybeList<number>> (as in 3.9.0-dev.20200324)

Related Issues:

37610, #37526, #33055, #30551, #35998, #37534, #37115, #34925

maybe also:
#9998, #33707, #36232, #35136, #33562, #34883, #31394
as discussed in TypeScript 3.9 Iteration Plan #37198

Needs More Info

Most helpful comment

haha i can't blame you tu add some joy on your code!
So if i understand correctly, 3.9.0 fixes the issue ?

All 10 comments

If it can help i have this example

Here the thing that break type inferring is the fact that one of the promises has unknown type even if its return value is not used

If it can help i have this example

Here the thing that breaks type inferring is the fact that one of the promises has unknown type even if its return value is not used

*I am sorry @adrienboulle I am not sure what I should be looking for in your example you mention I think the line 13 where getNumbersB() is not used but I don't see the problem ... *

Capture d’écran, le 2020-04-08 à 22 52 45

click to enlarge image -- #Pop N' Lock Theme by Luxcium ✨

At this moment I am still using 3.9.0-dev.20200324 because for some random reason I had that problem on 03-23 got that fix by chance on the 24th and it got reverted the day after ...

ok (OOPS!) I forgot to install the typescript@latest (3.8.3) and I think the typescript@next is not breaking (3.9.0-dev.20200408) so I can upgrade from 3.9.0-dev.20200324

Capture d’écran, le 2020-04-08 à 23 01 50

click to enlarge image -- #Pop N' Lock Theme by Luxcium ✨

yes I have « 🍒» inside of my error messages LoL 😹

haha i can't blame you tu add some joy on your code!
So if i understand correctly, 3.9.0 fixes the issue ?

I'm not really sure what's going on here; we need a concise problem description to work from

I'm not really sure what's going on here; we need a concise problem description to work from

At this moment I am stuck using 3.9.0-dev.20200324 any other version is not working for my project... Because when I am using generics the same type variable "T" is sometimes referring to a promise of T (Promise<T>) sometimes referring to a T to have a concrete example I was using a number type as my T type and was denoting T1and T2 a same T type (T1 and T2 is the same variable T but to talk about it in both ways I have used a number) ...

The awaited keyword is implicit and I don't know how to do without this concept...

I don't know how to explain it so
Instead of this:

    // Promise.all(this.fork) take a T[] where T is a Promise<number>
    // and return a Promise<T[]> where T is a number
    // therefore, T and T are not both the same  «T»

I have said this:

    // Promise.all(this.fork) take a T1[] where T1 is a Promise<number>
    // and return a Promise<T2[]> where T2 is a number
    // therefore T1 and T2 are not both «T»

The TypeScript playground site was not working correctly for me when I have first opened this issue it is now working ...

With: 3.9.0-dev.20200324 the following code awaitedPowersOneToTen.then(awaitedList => console.log(awaitedList)); when I mouse over awaitedList it is a MyMaybeList<number>

But with: 3.9.0-dev.20200420 the following code awaitedPowersOneToTen.then(awaitedList => console.log(awaitedList)); when I mouse over awaitedList it is a MyMaybeList<Promise<number>>

I am using Promise.all on something like an array or a list (MyMaybeList) and I want to infer the type without explicitly passing it (in a generic) ... Promise.all should remove the Promise inside of my list so I can get MyMaybeList<number> a list of "numbers" instead of gettingMyMaybeList<Promise<number>> a list of "Promises of a number" which is the type that I have before using Promise.all

I got this error message and don't know what I should do ...
error(TS2345): Argument of type 'awaited T' is not assignable to parameter of type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to 'awaited T'.

Capture d’écran, le 2020-05-04 à 17 38 39

Code as text below -- Click to enlarge image -- #Pop N' Lock Theme by Luxcium ✨

TypeScript code: lib/functional/promise-or-not.ts

// ... [more code]

export async function thenified<T>(
  promise: Promise<T>,
  funct: <R>(t: T) => R,
): Promise<any> {
  return promise.then(
    t => funct(t)
  );
}

Output: tsc Version 3.9.0-dev.20200324

% ❯  tsc # 3.9.0-dev.20200324 
lib/functional/promise-or-not.ts:10:16 - error TS2345: Argument of type 'awaited T' is not assignable to parameter of type 'T'.
  'T' could be instantiated with an arbitrary type which could be unrelated to 'awaited T'.

10     t => funct(t)
                  ~
Found 1 error.

Also on Stack Overflow: Argument of type 'awaited T' is not assignable to parameter of type 'T'

Playground 3.9.0-dev.20200324

Playground Nightly

Playground 3.8.3

Due to different other problems in my code when using other TS version I am stuck using Version 3.9.0-dev.20200324

In regard to the problem described above in my previous comment I could, meanwhile, do:

return promise.then(t => funct((t as any) as T));

or do:

return promise.then(t => funct((t as unknown) as T));

_I am not sure the difference between using unknown or any in this particular example ..._


This is when using Version 4.0.0-dev.20200504 other parts of code are not shown here, the problem is detailed above in this current GitHub issue #37664

Many more problems using current version

Was this page helpful?
0 / 5 - 0 ratings

Related issues

fdecampredon picture fdecampredon  ·  358Comments

nitzantomer picture nitzantomer  ·  135Comments

OliverJAsh picture OliverJAsh  ·  242Comments

xealot picture xealot  ·  150Comments

chanon picture chanon  ·  138Comments